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

Finding the shortest word ladder between two given words and a dictionary

本文关键字:之间 字典 单词阶 单词 两个      更新时间:2023-10-16

我正试图从字典中找到两个给定单词之间的最短阶梯。字典中的所有单词,包括给定的单词和,都有相同数量的字符。在一个过程中,只能更改一个字符,并且需要最短路径。例如:给定:"hit"answers"cil"骰子:["hil","hol","hot","lot","lit">

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

如果,我试着用26个字母替换每个字母,如果结果单词出现在字典中,接受这一点:仍然这种方法不会给我最短的路径。例:在这里,它会给我:点击->点击->批量->热门->霍尔->里尔->cil

也许,更好的方法是先构造一棵树,然后在树中找到从起始词到结束词的最短路径。

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

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

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

如果目标单词等于起始单词,或者Levenstein距离为1,则结果为[start, target],您就完成了。

否则,您必须查找字典中与起始词相距1的所有成员。如果其中一个单词到目标单词的Levenstein距离为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的字段。如果回溯将我们带回右下角的单元格,则没有解决方案。

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

现在我没有时间仔细考虑。如果这不是一个完整的解决方案,您可以将其用作优化步骤,而不是shortestWOrdLadder()第一行中的levenshtein(start, target)调用,因为该算法会为您提供Levenstein距离,如果可能的话,还会为您提供最短路径。

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

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

这是我写的代码(它经过我的测试并有效。任何错误或建议都将不胜感激。):

#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;
}