找到两个给定单词和字典之间的最短字梯

时间:2016-12-01 16:25:05

标签: c++ algorithm

我试图从两个给定单词之间的字典中找到最短的梯子。包括给定词和词典中的所有单词具有相同数量的字符。在一次通过中,只能更改一个字符并且需要最短路径。例如:给出:“命中”和“cil”Dic:[“hil”,“hol”,“hot”,“lot”,“lit”,“lil”]所以,答案应该是“命中” - &gt ; “HIL” - > “中CIL”

我试图用BFS解决这个问题;通过查找字典中的下一个单词并检查它是否与队列中的弹出项相邻。这种方法不会给我最短的路径:

如果,我尝试用26个字母替换每个字母,如果结果字出现在字典中,请接受:仍然这种方法不会给我最短的路径。例如:在这里,它应该给我:hit-> lit-> lot-> hot-> hol-> lil-> cil

可能更好的方法是首先构造一个树,然后在树中找到从开始单词到结束单词的最短路径。

我知道,这个论坛上有这个问题的解决方案,但没有一个解释算法。我是BFS的新手,所以不太熟悉。 我有兴趣知道如何找到最短路径之一,如果找到几条最短路径。

3 个答案:

答案 0 :(得分:1)

我建议在字典中的单词上构建一个图表,其中一个节点代表一个单词,并且有一个来自< - >的边缘。 b如果b可以通过仅改变a中的一个字符而转换为a(当然,反之亦然)。此过程将花费O(n * n)时间,其中n为no。字典中的单词。
如何做到这一点如下:
对于每个单词构建频率字符数组,将其称为farr,这是26长度,而farr [i]告诉字符i有多少次,按字母顺序出现在单词中,然后在一个嵌套循环中运行n * n次你只需比较单词频率表的条目,它们必须只有一个字符,以便从单词a到b有一个边缘。
另请注意,此图中的边缘是无向的(在两个方向上) 在构建字典单词的完整图形后,将问题单词添加到图形中。然后继续BFS从初始字节点搜索目标字,其中所需的变换是初始字 - >目标词。 现在说你在级别'i'找到目标词,而从初始词开始探索,那么最短路径是'i'单位长。

答案 1 :(得分:0)

这种做法有点暴力,但可能是一个很好的起点。

如果目标字词等于起始字词,或者Levenshtein distance 1,则结果为[start, target],您就完成了。

否则你必须从起始单词中找到Levenshtein距离1的所有字典成员。如果其中一个人与目标单词的距离为1,则结果为[start, word, target]并且您已完成。否则,您将所选列表中的每个单词作为开始并将目标作为目标递归,并将start添加到最短结果。

伪代码 - 有点像python:

myDict = {"hil", "hol", "hot", "lot", "lit", "lil"}

used_wordlist = {}

shortestWordLadder(start, target):
    if start == target or levenshtein(start, target) = 1:
        return [start, target]

    current_wordlist = [x for x in myDict
                              if x not in used_wordlist and
                              levenshtein(ladder[-1], x) = 1]

    if current_wordlist.size = 0:
        return null

    for word in current_wordlist:
        if levenshtein(word, target) == 1:
            return [start, word, target]

     used_wordlist.insert_all(current_wordlist)
     min_ladder_size = MAX_INT
     min_ladder = null

     for word in currrent_wordlist:
         ladder = shortestWordLadder(word, target)

         if ladder is not null and ladder.size < min_ladder_size:
             min_ladder_size = ladder.size
             min_ladder = ladder.prepend(start)

     return min_ladder

可能的优化:

我考虑重复使用levenshtein(start, target)内部创建的矩阵,但我无法获得足够的信心,它会在所有情况下都有效。我们的想法是从矩阵的右下角开始,选择最小的邻居,从字典中创建一个单词。然后继续这个位置。如果当前单元格的邻居没有从字典中创建单词,我们必须回溯,直到找到通向值为0的字段的方法。如果回溯将我们带回到右下方的单元格,则没有解决方案。

我现在不确定,可能没有解决方案,你可能会忽略这种方式。如果它找到了解决方案,我非常有信心,它是最短的解决方案之一。

目前我没有时间思考它。如果证明这不是一个完整的解决方案,您可以将其用作优化步骤而不是levenshtein(start, target)第一行中的shortestWOrdLadder()调用,因为算法会为您提供Levenshtein距离,如果可能的话,最短的路径。

答案 2 :(得分:0)

我采用以下方法制定了解决方案: 1.)我先从字典中建了一棵树,假设起点是给定的单词;并查找与该单词相邻的所有单词,依此类推 2.)接下来,我尝试使用这个树构建从给定单词到结束单词的所有可能路径。

复杂度:O(n * 70 + 2 ^ n-1 * lg(n))= O(2 ^ n-1 * lg(n))这里n是字典中的单词数,70出来作为从65到122(A到a)的ASCII值范围,我在这里采取了一个圆形的数字。如预期的那样,复杂性是指数级的。 即使经过一定的优化,最坏情况的复杂性也不会发生变化。

这是我写的代码(由我测试并且有效。任何错误或建议都会受到高度赞赏。):

#include <iostream>
#include <vector>
#include <cstring>
#include <deque>
#include <stack>
#include <algorithm>

using namespace std;

struct node {
    string str;
    vector<node *> children;
    node(string s) {
        str = s;
        children.clear();
    }
};

bool isAdjacent(string s1, string s2) {
    int table1[70], table2 [70];
    int ct = 0;

    for (int i = 0; i < 70; i++) {
        table1[i] = 0;
        table2[i] = 0;
    }
    for (int i = 0; i < s1.length(); i++) {
        table1[((int)s1[i])- 65] += 1;
        table2[((int)s2[i])- 65] += 1;
     }

     for (int i = 0; i < 70; i++) {
        if (table1[i] != table2[i])
            ct++;
        if (ct > 2)
            return false;
     }
     if (ct == 2)
        return true;
     else
        return false;
}

void construct_tree(node *root, vector<string> dict) {
    deque<node *> q;
    q.push_back(root);
    while (!q.empty()) {
        node *curr = q.front();
        q.pop_front();
        if (dict.size() == 0)
            return;
        for (int i = 0; i < dict.size(); i++) {
            if (isAdjacent(dict[i], curr->str)) {
                string n = dict[i];
                dict.erase(dict.begin()+i);
                i--;
                node *nnode = new node(n);
                q.push_back(nnode);
                curr->children.push_back(nnode);
            }
        }
    }
}

void construct_ladders(stack<node *> st, string e, vector<vector <string> > &ladders) {
    node *top = st.top();
    if (isAdjacent(top->str,e)) {
        stack<node *> t = st;
        vector<string> n;
        while (!t.empty()) {
            n.push_back(t.top()->str);
            t.pop();
        }
        ladders.push_back(n);
    }
    for (int i = 0; i < top->children.size(); i++) {
        st.push(top->children[i]);
        construct_ladders(st,e,ladders);
        st.pop();
    }
}

void print(string s, string e, vector<vector<string> > ladders) {
    for (int i = 0; i < ladders.size(); i++) {
        for (int j = ladders[i].size()-1; j >= 0; j--) {
            cout<<ladders[i][j]<<" ";
        }
        cout<<e<<endl;
    }
}

int main() {
    vector<string> dict;
    string s = "hit";
    string e = "cog";

    dict.push_back("hot");
    dict.push_back("dot");
    dict.push_back("dog");
    dict.push_back("lot");
    dict.push_back("log");

    node *root = new node(s);
    stack<node *> st;
    st.push(root);

    construct_tree(root, dict);

    vector<vector<string> > ladders;
    construct_ladders(st, e, ladders);

    print(s,e,ladders);

    return 0;
}