这是一个双重问题,因为我没有提出如何最有效地实现这一目标的想法。
我有一个150,000字的字典,存储在Trie实现中,这是我的特定实现:
向用户提供两个单词。目标是从起始单词到结束单词找到其他英语单词的最短路径(由一个字符改变)。
例如:
开始:狗
结束:猫
路径:狗,点,婴儿床,猫
路径:狗,Cog,Log,Bog,Bot,Cot,Cat
路径:狗,Doe,Joe,Joy,Jot,Cot,Cat
我当前的实现经历了几次迭代,但最简单的我可以提供伪代码(因为实际代码是几个文件):
var start = "dog";
var end = "cat";
var alphabet = [a, b, c, d, e .... y, z];
var possible_words = [];
for (var letter_of_word = 0; letter_of_word < start.length; letter_of_word++) {
for (var letter_of_alphabet = 0; letter_of_alphabet < alphabet.length; letter_of_alphabet++) {
var new_word = start;
new_word.characterAt(letter_of_word) = alphabet[letter_of_alphabet];
if (in_dictionary(new_word)) {
add_to.possible_words;
}
}
}
function bfs() {
var q = [];
... usual bfs implementation here ..
}
的已知,:
<小时/>
我的问题是我没有一种有效的方法来确定一个潜在的单词,可以在不强制字母表的情况下尝试并根据字典检查每个新单词。我知道有可能采用更有效的方式使用前缀,但我无法弄清楚一个正确的实现,或者一个不会使处理加倍的实现。
其次,如果我使用不同的搜索算法,我会将A *和最佳优先搜索作为可能性,但那些需要权重,这是我没有的。
思想?
答案 0 :(得分:4)
根据评论中的要求,通过对整数位中的链接字进行编码来说明我的意思。
在C ++中,它可能看起来像......
// populate a list of known words (or read from file etc)...
std::vector<std::string> words = {
"dog", "dot", "cot", "cat", "log", "bog"
};
// create sets of one-letter-apart words...
std::unordered_map<std::string, int32_t> links;
for (auto& word : words)
for (int i = 0; i < word.size(); ++i)
{
char save = word[i];
word[i] = '_';
links[word] |= 1 << (save - 'a');
word[i] = save;
}
在上面的代码运行后,links[x]
- 其中x
是一个单词,其中一个字母替换为下划线la d_g
- 检索一个整数,表示可以替换下划线的字母形成已知的词。如果最低有效位开启,那么'dag'是一个已知字,如果从最低有效位开始,则'dbg'是已知字等。
直观地说,我希望使用整数来减少用于链接数据的总内存,但如果大多数单词每个只有几个链接的单词,那么存储一些索引或指向这些单词的指针实际上可能会使用更少的内存 - 并且如果您不习惯按位操作,则更容易,例如:
std::unordered_map<std::string, std::vector<const char*>> links;
for (auto& word : words)
for (int i = 0; i < word.size(); ++i)
{
char save = word[i];
word[i] = '_';
links[word].push_back(word.c_str());
word[i] = save;
}
无论哪种方式,您都可以使用图表将每个单词与可以转换为单个字符更改的单词相关联。然后,您可以应用Dijkstra's algorithm的逻辑来查找任意两个单词之间的最短路径。
答案 1 :(得分:1)
只是为那些主演此问题的人添加更新,我已经在Javascript中为这个特定的数据结构添加了一个Github存储库。
https://github.com/acupajoe/Lexibit.js
谢谢大家的帮助和想法!