生成所有可能的字符串组合算法,从给定字符串生成到两个字母。

8

算法以给定字符串为基础生成所有可能的字母组合,最小长度为2个字母。

我正在尝试在AS3中创建一个类似于这里的变位词求解器。

我遇到了一个问题,就是如何为不同长度的字符串生成所有可能的字母组合。如果我只是为一个固定长度生成排列,那对我来说就不是问题了……但我想缩短字符串的长度,并从原始字母集合中获取所有可能的排列,以获得长度小于原始字符串的最大长度的字符串。例如,假设我想要一个长度为2的字符串,但我有一个三个字母的字符串“abc”,输出将是:ab ac ba bc ca cb。

理想情况下,该算法将生成一个完整的可能组合列表,从原始字符串长度开始,一直到最小的字符串长度为2。我感觉可能有一个小递归算法可以做到这一点,但我没能理解它。我正在AS3中工作。

谢谢!


最好的方法可能是按顺序查找组合,并在哈希表中查找每个组合,将排序后的字母字符串映射到可以作为排列形式形成的单词。 - user287792
5个回答

8
为了编写像你提供的链接一样的字谜求解器,你所请求的算法并非必要,并且非常昂贵。
以一个6个字母的单词"MONKEY"为例。该单词的6个字母都不同,因此您需要创建:
- 6*5*4*3*2*1 种不同的6个字母单词 - 6*5*4*3*2 种不同的5个字母单词 - 6*5*4*3 种不同的4个字母单词 - 6*5*4 种不同的3个字母单词 - 6*5 种不同的2个字母单词 - 总计1950个单词
现在,假设您不想将所有1950个单词(例如 "OEYKMN")作为字谜输出(尽管它们确实是字谜,但其中大多数也是无意义的)。我猜您拥有一个合法的英语单词字典,只需检查这些单词是否与查询词的字谜匹配,还可以选择不使用所有字母。
如果是这种情况,那么问题就很简单。
要确定两个单词是否为字谜,您只需要计算每个字母使用次数的数量,并比较这些数字!
让我们仅限于26个字母A-Z,大小写不敏感。您需要编写一个名为"countLetters"的函数,该函数接受一个单词并返回一个包含26个数字的数组。数组中的第一个数字对应于单词中字母"A"的计数,第二个数字对应于字母"B"的计数等等。
然后,当且仅当每个i都满足"countLetters(W1)[i] == countLetters(W2)[i]"时,两个单词W1和W2是完全的字谜!也就是说,每个单词使用每个字母的数量完全相同!
对于我所说的子字谜(例如"MONEY"是"MONKEY"的一个子字谜),当且仅当每个i都满足"countLetters(W1)[i] <= countLetters(W2)[i]"时,W1是W2的子字谜!也就是说,子字谜可以少用某些字母,但不可以多用!
(注意:"MONKEY"也是"MONKEY"的一个子字谜)。
这将为您提供足够快的算法,当给定一个查询字符串时,您只需阅读一次字典,将每个单词的字母计数数组与查询单词的字母计数数组进行比较。您可以进行一些小优化,但这应该已经足够了。

如果您想要最大的性能,可以预处理字典(提前知道的),并创建子异序词关系的有向无环图。

以下是这样一个图的一部分,以供说明:

 D=1,G=1,O=1  ----------> D=1,O=1
  {dog,god}   \            {do,od}
               \
                \-------> G=1,O=1
                           {go}

基本上,每个节点都是一个桶,用于存放所有具有相同字母计数数组的单词(即它们是精确的字谜)。然后如果N2的数组是<=(如上所定义)N1的数组,则从N1N2存在一个节点(您可以执行可达性缩减以存储最少量的边)。
然后要列出单词的所有子谜题,您只需找到与其字母计数数组对应的节点,并递归地探索从该节点可访问的所有节点。它们的桶将包含子谜题。

4
以下 JavaScript 代码将查找 n 个字母单词中所有可能的“单词”。当然,这并不意味着它们是真正的单词,但可以给你所有的组合。在我的机器上,对于一个七个字母的单词,大约需要0.4秒,而对于一个九个字母的单词,则需要15秒(如果没有重复的字母,则有近一百万种可能性)。但是这些时间包括查找字典和找出哪些是真正的单词。
var getWordsNew=function(masterword){
var result={}
 var a,i,l;
function nextLetter(a,l,key,used){
     var i;
    var j;
    if(key.length==l){
        return;
    }
    for(i=0;i<l;i++){
        if(used.indexOf(""+i)<0){
            result[key+a[i]]="";
            nextLetter(a,l,key+a[i],used+i);
        }
    }
 }
a=masterword.split("");
  l=a.length;
for (i = 0; i < a.length; i++) {
    result[a[i]] = "";
    nextLetter(a, l, a[i], "" + i)
}
return result;
}

代码完整版请访问:

使用递归查找单词中的单词


0

你想要一种排列方式。如果你熟悉排列算法,那么你知道你需要检查何时生成足够的数字。只需更改该限制:

我不知道AS3,但这是一个伪代码:

st = an array
Arrangements(LettersInYourWord, MinimumLettersInArrangement, k = 1)
  if ( k > MinimumLettersInArrangements )
  {
    print st;
  }

  if ( k > LettersInYourWord )
    return;      

  for ( each position i in your word that hasn't been used before )
    st[k] = YourWord[i];
    Arrangements(<same>, <same>, k + 1);

对于 "abc" 和 Arrangements(3, 2, 1); 这将打印:

ab
abc
ac
acb
...

如果你想先显示三个字符的,然后再显示两个字符的,请考虑以下代码:
st = an array
Arrangements(LettersInYourWord, DesiredLettersInArrangement, k = 1)
  if ( k > DesiredLettersInArrangements )
  {
    print st;
    return
  }

  for ( each position i in your word that hasn't been used before )
    st[k] = YourWord[i];
    Arrangements(<same>, <same>, k + 1);

然后对于 "abc",调用 Arrangements(3, 3, 1); 然后调用 Arrangements(3, 2, 1);

0

你可以通过在字母的完全图中找到所有路径来生成字母表中的所有单词。你可以通过从每个字母开始进行深度优先搜索,并在每个点返回当前路径来找到该图中的所有路径。


0

有一个简单的O(N)算法,其中N是词汇表的大小。 只需对词汇表中每个单词中的字母进行排序,或者更好地创建它们的二进制掩码,然后与您拥有的字母进行比较。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接