最短词列表的算法

时间:2017-05-27 17:22:33

标签: c# algorithm

问题如下:用户提供一个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次更改)

我一直在研究一些匹配的算法和递归,但我无法弄清楚如何解决这个问题。

提前感谢您提供任何帮助。

4 个答案:

答案 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”来检查。结果如下

enter image description here

答案 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;
}