问题如下:用户提供一个X字母的StartWord和EndWord字符串以及长度为X的字符串列表(让它变为4但可能更多)
static void Main(string[] args)
{
string StartWord = "Spot";
string EndWord = "Spin";
List<string> providedList = new List<string>
{
"Spin", "Spit", "Spat", "Spot", "Span"
};
List<string> result = MyFunc(StartWord, EndWord, providedList);
}
public List<string> MyFunc(string startWord, string endWord, List<string> input)
{
???
}
根据提供的参数,我需要向用户显示一个结果,该结果包含4个字母单词的SHORTEST列表,以StartWord开头,以EndWord结尾,列表中有多个中间词,其中每个单词与前一个单词的区别在于PRECISELY一个字母。
例如,上面的代码应该返回包含这些元素的字符串列表: Spot(作为FirstWord), 吐(只有一个字母与前一个字不同), 旋转(作为EndWord)
一个糟糕的例子是:Spot,Spat,Span,Spin(因为与上面的2相比需要3次更改)
我一直在研究一些匹配的算法和递归,但我无法弄清楚如何解决这个问题。
提前感谢您提供任何帮助。
答案 0 :(得分:2)
创建一个顶点为单词的图形,一条边连接任意两个字母相差不多的单词。
从StartWord
开始,进行广度优先搜索,寻找EndWord
的最短路径。
以下是使用其他语言(Python)的此解决方案的示例代码。这可能会给你一个更好的指针。 : - )
def shortestWordPath (startWord, endWord, words):
graph = {}
for word in words:
graph[word] = {"connected": []}
for word in words:
for otherWord in words:
if 1 == wordDistance(word, otherWord):
graph[word]['connected'].append(otherWord)
todo = [(startWord,0)]
while len(todo):
(thisWord, fromWord) = todo.pop(0)
if thisWord == endWord:
answer = [thisWord, fromWord]
while graph[ answer[-1] ]["from"] != 0:
answer.append(graph[ answer[-1] ]["from"])
answer.reverse()
return answer
elif "from" in graph[thisWord]:
pass # We have already processed this.
else:
graph[thisWord]["from"] = fromWord
for nextWord in graph[thisWord]["connected"]:
todo.append([nextWord, thisWord])
return None
def wordDistance (word1, word2):
return len(differentPositions(word1, word2))
def differentPositions(word1, word2):
answer = []
for i in range(0, min(len(word1), len(word2))):
if word1[i] != word2[i]:
answer.append(i)
for i in range(min(len(word1), len(word2)),
max(len(word1), len(word2))):
answer.append(i)
return answer
print shortestWordPath("Spot", "Spin",
["Spin", "Spit", "Spat", "Spot", "Span"])
答案 1 :(得分:0)
下面的代码列出了所有可能的列表。
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Data data = new Data();
data.StartWord = "Spot";
data.EndWord = "Spin";
data.ProvidedList = new List<string> { "Spin", "Spit", "Spat", "Spot", "Span", /* I added this word to chek */ "Spon" };
data.BeginMyAlgorithm();
// Sort lists by Count
data.ListOfLists.Sort((list1, list2) => list1.Count - list2.Count);
foreach (List<string> list in data.ListOfLists)
{
Console.WriteLine();
Console.WriteLine();
for (int i = list.Count - 1; i >= 0; i--)
{
Console.WriteLine(list[i]);
}
}
Console.Read();
}
class Data
{
public string StartWord = "Spot";
public string EndWord = "Spin";
public List<string> ProvidedList;
public List<List<string>> ListOfLists = new List<List<string>>();
public void BeginMyAlgorithm()
{
ListOfLists.Clear();
Stack<string> stack = new Stack<string>();
stack.Push(StartWord);
MyAlgorithm(stack);
}
public void MyAlgorithm(Stack<string> stack)
{
string word = stack.Peek();
if (CountDifference(word, EndWord) == 0)
{
//Save list and return
ListOfLists.Add(new List<string>(stack));
return;
}
int n = ProvidedList.Count;
for (int i = 0; i < n; i++)
{
string word2 = ProvidedList[i];
if (stack.Contains(word2)) continue;
if (CountDifference(word2, word) == 1)
{
stack.Push(word2);
MyAlgorithm(stack);
stack.Pop();
}
}
}
}
static int CountDifference (string word1, string word2)
{
int length = word1.Length;
int count = 0;
for (int i = 0; i < length; i++)
{
if (word1[i] != word2[i]) count++;
}
return count;
}
}
}
我又添加了一个字“Spon”来检查。结果如下
答案 2 :(得分:0)
我还没有足够的声誉来评论,但是btilly的解决方案不适用于需要每个单词更改1次的情况(西班牙 - Drain)。
我想要的方法确实是创建一个图形,其中顶点是单词,但在每两个顶点(单词)之间连接一个加权边,其中权重表示它们之间的距离(意思是将一个单词转换为另一个单词所需的更改次数)。请注意,您可以保证具有非负边缘。然后我会应用Dijkstra的算法(https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)来寻找最短路径(源节点是StartWord)。
请注意,虽然更简单的解决方案是列出所有可能的路径并选择最短的路径,但这将是可怕的复杂性((n-2)!可能的路径)
答案 3 :(得分:0)
这就是我最终使用的内容(请随时评论它的上下):
private List<List<string>> allWordSteps;
private string[] allWords;
public List<string> WordLadder(string wordStart, string wordEnd, string[] allWordsInput)
{
var wordLadder = new List<string>() { wordStart };
this.allWordSteps = new List<List<string>>() { wordLadder };
allWords = allWordsInput;
do
{
wordLadder = this.IterateWordSteps(wordEnd);
}
while (wordLadder.Count() == 0);
return wordLadder;
}
private List<string> IterateWordSteps(string wordEnd)
{
List<List<string>> allWordStepsCopy = this.allWordSteps.ToList();
this.allWordSteps.Clear();
foreach (var wordSteps in allWordStepsCopy)
{
var adjacent = this.allWords.Where(
x => this.IsOneLetterDifferent(x, wordSteps.Last()) &&
!wordSteps.Contains(x));
if (adjacent.Contains(wordEnd))
{
wordSteps.Add(wordEnd);
return wordSteps;
}
foreach (var word in adjacent)
{
List<string> newWordStep = wordSteps.ToList();
newWordStep.Add(word);
this.allWordSteps.Add(newWordStep);
}
}
return new List<string>();
}
private bool IsOneLetterDifferent(string first, string second)
{
int differences = 0;
if (first.Length == second.Length)
{
for (int i = 0; i < first.Length; i++)
{
if (first[i] != second[i])
{
differences++;
}
}
}
return differences == 1;
}