/// <summary> /// Testing algorithm /// </summary> /// <param name="args"></param> public static List<string> WordToWord(string sIni, string sEnd) { List<string> lSolution = new List<string>(); Words.WordDictionary wordsDictionary = new WordDictionary(); // Find a Solution System.Diagnostics.Debug.WriteLine("FindSecuenceFirst:"); List<string> lSecuence = new List<string>(); FindSecuenceFirst(sIni, sEnd, wordsDictionary, null, ref lSecuence); if (lSecuence.Count == 0) { System.Diagnostics.Debug.WriteLine("Path not found"); } else { System.Diagnostics.Debug.WriteLine(string.Format("[{0} Steps] {1} {2} {3}", lSecuence.Count, sIni, lSecuence.ToStringItem(), sEnd)); } // Find the shorted path to a solution System.Diagnostics.Debug.WriteLine("FindSecuenceAll:"); List<string> lMinSecuence = new List<string>(); int iMin = int.MaxValue; FindSecuenceMin(sIni, sEnd, wordsDictionary, null, null, ref iMin, ref lMinSecuence); if (iMin == int.MaxValue) { System.Diagnostics.Debug.WriteLine("Path not found"); } else { System.Diagnostics.Debug.WriteLine(string.Format("[{0} Steps] {1} {2} {3}", lMinSecuence.Count, sIni, lMinSecuence.ToStringItem(), sEnd)); } return lMinSecuence; }
/// <summary> /// Return existing words changing only one letter /// </summary> /// <param name="sOldString">Initial string</param> /// <returns></returns> private static List<string> GetNewStrings(string sWord, WordDictionary dicc) { List<string> listNewStrings = new List<string>(); if (string.IsNullOrEmpty(sWord)) { sWord = string.Empty; } if (dicc != null) { for (int i = 0; i < sWord.Length; i++) { char letter = sWord.ToLower()[i]; for (char l = 'a'; l <= 'z'; l++) { if (l == sWord[i]) continue; StringBuilder sNewWord = new StringBuilder(sWord); sNewWord[i] = l; if (dicc.Contains(sNewWord.ToString())) { listNewStrings.Add(sNewWord.ToString()); } } } } return listNewStrings; }
/// <summary> /// Find a solution (stop recursion when first solution is found) /// </summary> /// <param name="sIni"></param> /// <param name="sEnd"></param> /// <param name="dicc"></param> /// <param name="lPreviousExplored"></param> /// <param name="lResultSecuence"></param> /// <returns></returns> /// /// Bactracking template: Find single solution /// /// findSolutions(n, other params) /// { /// /// if (found a solution) { /// displaySolution(); /// return true; /// } /// /// for (val = first to last) /// { /// if (isValid(val, n)) /// { /// applyValue(val, n); /// /// if (findSolutions(n + 1, other params)) /// return true; /// /// removeValue(val, n); /// } /// } /// /// return false; /// } private static bool FindSecuenceFirst(string sIni, string sEnd, WordDictionary dicc, List<string> lPreviousExplored, ref List<string> lResultSecuence) { // Validate input if (lPreviousExplored == null) { lPreviousExplored = new List<string>(); } if (lResultSecuence == null) { lResultSecuence = new List<string>(); } if (iDistance (sIni, sEnd) == 1) { // Solution found! System.Diagnostics.Debug.WriteLine(String.Format("Path with solution: {0}", lResultSecuence.ToStringItem())); return true; } else { // Available choices List<string> lCandidate = GetNewStrings(sIni, dicc); lCandidate = lCandidate.Except(lPreviousExplored).ToList(); lPreviousExplored.AddRange(lCandidate); // No more choices! if (lCandidate.Count == 0) { // Path without solution // System.Diagnostics.Debug.WriteLine(String.Format("Path without solution: {0}", lResultSecuence.ToStringItem())); return false; } // Explore all available choices foreach (string s in lCandidate) { lResultSecuence.Add(s); if (FindSecuenceFirst(s, sEnd, dicc, lPreviousExplored, ref lResultSecuence)) { // Stop recursion when a solution is found return true; } lResultSecuence.RemoveAt(lResultSecuence.Count - 1); } return false; } }
/// <summary> /// Find shorted path to a solution (explore all path in order to find the minimun number of intermediate words) /// </summary> /// <param name="sIni"></param> /// <param name="sEnd"></param> /// <param name="dicc"></param> /// <param name="lPreviousExplored"></param> /// <param name="lStepSecuence"></param> /// /// Bactracking template: Find all solution /// /// void findSolutions(n, other params) { /// /// if (found a solution) { /// solutionsFound++; /// displaySolution(); /// if (solutionsFound >= solutionTarget) /// System.exit(0); /// return; /// } /// /// for (val = first to last) { /// if (isValid(val, n)) { /// applyValue(val, n); /// findSolutions(n + 1, other params); /// removeValue(val, n); /// } /// } /// /// } private static void FindSecuenceMin(string sIni, string sEnd, WordDictionary dicc, Dictionary<string, int> diccPreviousExplored, List<string> lStepSecuence, ref int iMin, ref List<string> lMinSecuence) { // Validate input if (diccPreviousExplored == null) { diccPreviousExplored = new Dictionary<string, int>(); } if (lStepSecuence == null) { lStepSecuence = new List<string>(); } if (lStepSecuence.Count >= iMin) { // A previous solution is better. Stop exploring this branch. return; } if (iDistance(sIni, sEnd) == 1) { // Solution found! iMin = lStepSecuence.Count; lMinSecuence = new List<string>(lStepSecuence); //System.Diagnostics.Debug.WriteLine(String.Format("Path with solution ({0} words): {1}", lStepSecuence.Count, lStepSecuence.ToStringItem())); return; } else { // Available choices List<string> lCandidate = GetNewStrings(sIni, dicc); // Remove a choice if it have been visited in previous stage int iPosition = lStepSecuence.Count; for (int i=lCandidate.Count-1; i>=0; i--) { if (!diccPreviousExplored.ContainsKey (lCandidate[i])) { // Choice not explored yet diccPreviousExplored.Add(lCandidate[i], iPosition); } else { // Choice explored before if (diccPreviousExplored[lCandidate[i]] < iPosition) { // The explored options is better, the word was in a low secuence position. // Discard this choice! lCandidate.RemoveAt(i); } else { // Current options is better, the word currently is in a low secuence position. // Give it a chance... diccPreviousExplored[lCandidate[i]] = iPosition; } } } // Explore available choices foreach (string s in lCandidate) { lStepSecuence.Add(s); // Continue recursion in order to found all solution FindSecuenceMin(s, sEnd, dicc, diccPreviousExplored, lStepSecuence, ref iMin, ref lMinSecuence); lStepSecuence.RemoveAt(lStepSecuence.Count - 1); } } }
/// <summary> /// Get a list of words given a secuence of phone keyboards numbers /// </summary> /// <param name="listNumber"></param> /// <returns></returns> public static List<string> GetWords(List<int> listNumber) { List<string> listWords = new List<string>(); // Validate input if (listNumber == null || listNumber.Count == 0) { throw new ArgumentNullException("listNumber", "List null or empty"); } if (listNumber.Exists (p=> p < 2 || p > 9)) { throw new ArgumentOutOfRangeException("listNumber", "Numbers must be in range 2 - 9"); } // Initialize queue Queue<string> queue = new Queue<string>(); foreach (string s in GetNewStrings(string.Empty, listNumber[0])) { queue.Enqueue(s); } // Generate possible words for (int i=1; i<listNumber.Count; i++) { while (queue.Count > 0 && queue.Peek().Count() <= i) { foreach (string s in GetNewStrings(queue.Dequeue(), listNumber[i])) { queue.Enqueue(s); } } } // Check possible words with a dictionary WordDictionary dicc = new WordDictionary(); while (queue.Count > 0) { string sAux = queue.Dequeue(); if (dicc.Contains(sAux)) { listWords.Add(sAux); } // Add all words generate // listWords.Add(queue.Dequeue()); } return listWords; }