你面临的问题可以分解为两个子问题:
- 找到使用输入字符串所有字符的单词组合
- 找到第一个子问题中找到的单词的所有排列
子问题#2是基本算法,你可以在大多数编程语言中找到现有的标准实现。让我们专注于子问题#1。
首先将输入字符串转换为“字符池”。我们可以将字符池实现为数组
oc
,其中
oc[c]
= 字符c出现的次数。
然后,我们使用回溯算法来查找适合字符池的单词,如下伪代码所示:
result = empty;
function findAnagram(pool)
if (pool empty) then print result;
for (word in dictionary) {
if (word fit in charpool) {
result = result + word;
update pool to exclude characters in word;
findAnagram(pool);
restore pool;
restore result;
}
}
}
注意:如果我们通过值传递charpool,那么我们就不需要恢复它了。但是由于它非常大,我更喜欢通过引用传递它。
现在我们删除冗余的结果并应用一些优化:
- 假设A在字典中出现在B之前。如果我们选择第一个单词是B,那么我们就不必考虑单词A在后续步骤中的情况,因为这些结果(如果我们选A)已经包含在选择A作为第一个单词的情况下。
- 如果字符集足够小(<64个字符最好),我们可以使用位掩码快速过滤无法放入池中的单词。位掩码标记哪些字符在单词中出现,无论它出现多少次。
更新伪代码以反映这些优化:
function findAnagram(charpool, minDictionaryIndex)
pool_bitmask <- bitmask(charpool);
if (pool empty) then print result;
for (word in dictionary AND word's index >= minDictionaryIndex) {
// bitmask of every words in the dictionary should be pre-calculated
word_bitmask <- bitmask(word)
if (word_bitmask contains bit(s) that is not in pool_bitmask)
then skip this for iteration
if (word fit in charpool) {
result = result + word;
update charpool to exclude characters in word;
findAnagram(charpool, word's index);
// as with any backtracking algorithm, we have to restore global states
restore pool;
restore result;
}
}
}
这是我对子问题#1的C++实现,其中字符集仅包含小写字母'a'..'z': http://ideone.com/vf7Rpl。