【问题标题】:Generate list of all possible permutations of a string生成字符串所有可能排列的列表
【发布时间】:2008-08-02 06:57:57
【问题描述】:

我将如何生成一个长度在 x 和 y 字符之间的字符串的所有可能排列的列表,其中包含一个可变的字符列表。

任何语言都可以,但它应该是可移植的。

【问题讨论】:

标签: string language-agnostic cross-platform


【解决方案1】:

有几种方法可以做到这一点。常用方法使用递归、记忆化或动态编程。基本思想是生成所有长度为 1 的字符串的列表,然后在每次迭代中,对于上一次迭代中生成的所有字符串,分别添加与字符串中的每个字符连接的字符串。 (下面代码中的变量 index 会跟踪上一次和下一次迭代的开始)

一些伪代码:

list = originalString.split('')
index = (0,0)
list = [""]
for iteration n in 1 to y:
  index = (index[1], len(list))
  for string s in list.subset(index[0] to end):
    for character c in originalString:
      list.add(s + c)

然后您需要删除所有长度小于 x 的字符串,它们将是列表中的第一个 (x-1) * len(originalString) 条目。

【讨论】:

  • 为什么要先存储元素列表,然后再清除呢? (参考伪代码中的第 1 行和第 3 行)。
  • @Jaseem 来自问题:“长度在 x 和 y 字符之间的字符串的所有可能排列”
【解决方案2】:

最好使用回溯

#include <stdio.h>
#include <string.h>

void swap(char *a, char *b) {
    char temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

void print(char *a, int i, int n) {
    int j;
    if(i == n) {
        printf("%s\n", a);
    } else {
        for(j = i; j <= n; j++) {
            swap(a + i, a + j);
            print(a, i + 1, n);
            swap(a + i, a + j);
        }
    }
}

int main(void) {
    char a[100];
    gets(a);
    print(a, 0, strlen(a) - 1);
    return 0;
}

【讨论】:

    【解决方案3】:

    你会得到很多字符串,这是肯定的......


    其中 x 和 y 是您定义它们的方式,而 r 是我们从中选择的字符数——如果我理解正确的话。您绝对应该根据需要生成这些,而不是草率地说,生成一个 powerset 然后过滤字符串的长度。

    以下绝对不是生成这些的最佳方法,但除此之外它很有趣。

    Knuth(第 4 卷,第 2 分册,7.2.1.3)告诉我们,(s,t)-组合等价于 s+1 个事物,每次 t 重复——一个 (s,t)-组合是Knuth 使用的符号等于。我们可以通过首先生成二进制形式的每个 (s,t) 组合(因此,长度为 (s+t))并计算每个 1 左侧的 0 的数量来解决这个问题。

    10001000011101 --> 变成排列:{0, 3, 4, 4, 4, 1}

    【讨论】:

      【解决方案4】:

      根据 Knuth 的非递归解决方案,Python 示例:

      def nextPermutation(perm):
          k0 = None
          for i in range(len(perm)-1):
              if perm[i]<perm[i+1]:
                  k0=i
          if k0 == None:
              return None
      
          l0 = k0+1
          for i in range(k0+1, len(perm)):
              if perm[k0] < perm[i]:
                  l0 = i
      
          perm[k0], perm[l0] = perm[l0], perm[k0]
          perm[k0+1:] = reversed(perm[k0+1:])
          return perm
      
      perm=list("12345")
      while perm:
          print perm
          perm = nextPermutation(perm)
      

      【讨论】:

      • 实际上,当字符串未排序时,这 not 起作用。如果您尝试使用 "54321",则仅显示 ONE 字符串(本身)。
      • 有趣的是nextPermutation() 是无状态的——它只需要输入进行置换,并且不会在每次迭代中维护索引。它可以通过假设初始输入已排序并根据维护排序的位置自行查找索引(k0l0)来做到这一点。对像“54321”->“12345”这样的输入进行排序将允许该算法找到所有预期的排列。但是,由于它需要做大量额外的工作来为它生成的每个排列重新查找这些索引,因此有更有效的方法可以非递归地执行此操作。
      【解决方案5】:

      您可能会查看“Efficiently Enumerating the Subsets of a Set”,它描述了一种算法来完成您想要的部分工作 - 快速生成从长度 x 到 y 的 N 个字符的所有子集。它包含一个 C 语言的实现。

      对于每个子集,您仍然需要生成所有排列。例如,如果你想要“abcde”中的 3 个字符,这个算法会给你“abc”、“abd”、“abe”...... 但是你必须排列每一个才能得到“acb”、“bac”、“bca”等。

      【讨论】:

        【解决方案6】:

        一些基于Sarp's answer的工作Java代码:

        public class permute {
        
            static void permute(int level, String permuted,
                            boolean used[], String original) {
                int length = original.length();
                if (level == length) {
                    System.out.println(permuted);
                } else {
                    for (int i = 0; i < length; i++) {
                        if (!used[i]) {
                            used[i] = true;
                            permute(level + 1, permuted + original.charAt(i),
                               used, original);
                            used[i] = false;
                        }
                    }
                }
            }
        
            public static void main(String[] args) {
                String s = "hello";
                boolean used[] = {false, false, false, false, false};
                permute(0, "", used, s);
            }
        }
        

        【讨论】:

        • 作为注释,请注意对于具有重复字符的字符串,这不会产生唯一的排列。这可以通过哈希来解决,但这可能是长字符串的问题。
        • 您可能希望使用 char 数组而不是字符串来加快运行速度,因为字符串在 java 中是不可变的。
        【解决方案7】:

        这是一个简单的 C# 解决方案。

        它只生成给定字符串的不同排列。

            static public IEnumerable<string> permute(string word)
            {
                if (word.Length > 1)
                {
        
                    char character = word[0];
                    foreach (string subPermute in permute(word.Substring(1)))
                    {
        
                        for (int index = 0; index <= subPermute.Length; index++)
                        {
                            string pre = subPermute.Substring(0, index);
                            string post = subPermute.Substring(index);
        
                            if (post.Contains(character))
                                    continue;                       
        
                            yield return pre + character + post;
                        }
        
                    }
                }
                else
                {
                    yield return word;
                }
            }
        

        【讨论】:

          【解决方案8】:

          这里有很多很好的答案。我还建议在 C++ 中使用非常简单的递归解决方案。

          #include <string>
          #include <iostream>
          
          template<typename Consume>
          void permutations(std::string s, Consume consume, std::size_t start = 0) {
              if (start == s.length()) consume(s);
              for (std::size_t i = start; i < s.length(); i++) {
                  std::swap(s[start], s[i]);
                  permutations(s, consume, start + 1);
              }
          }
          
          int main(void) {
              std::string s = "abcd";
              permutations(s, [](std::string s) {
                  std::cout << s << std::endl;
              });
          }
          

          注意:重复字符的字符串不会产生唯一的排列。

          【讨论】:

            【解决方案9】:

            我刚刚在 Ruby 中快速完成了这个:

            def perms(x, y, possible_characters)
              all = [""]
              current_array = all.clone
              1.upto(y) { |iteration|
                next_array = []
                current_array.each { |string|
                  possible_characters.each { |c|
                    value = string + c
                    next_array.insert next_array.length, value
                    all.insert all.length, value
                  }
                }
                current_array = next_array
              }
              all.delete_if { |string| string.length < x }
            end
            

            您可能会研究用于内置排列类型函数的语言 API,并且您可能能够编写更优化的代码,但如果数字都这么高,我不确定是否有很多方法可以解决很多结果。

            无论如何,代码背后的想法是从长度为 0 的字符串开始,然后跟踪所有长度为 Z 的字符串,其中 Z 是迭代中的当前大小。然后,遍历每个字符串并将每个字符附加到每个字符串上。最后,删除任何低于 x 阈值的内容并返回结果。

            我没有使用可能毫无意义的输入(空字符列表、x 和 y 的奇怪值等)对其进行测试。

            【讨论】:

            • 此代码错误。它将生成无效的排列,例如具有重复字符的排列。例如,对于字符串“abc”,它会生成以下大小为 3 的排列:[“aaa”、“aab”、“aac”、“aba”、“abb”、“abc”、“aca”、“acb”、 “acc”、“baa”、“bab”、“bac”、“bba”、“bbb”、“bbc”、“bca”、“bcb”、“bcc”、“caa”、“cab”、“cac” "、"cba"、"cbb"、"cbc"、"cca"、"ccb"、"ccc"]。这是不正确的。
            【解决方案10】:

            这是将 Mike 的 Ruby 版本翻译成 Common Lisp:

            (defun perms (x y original-string)
              (loop with all = (list "")
                    with current-array = (list "")
                    for iteration from 1 to y
                    do (loop with next-array = nil
                             for string in current-array
                             do (loop for c across original-string
                                      for value = (concatenate 'string string (string c))
                                      do (push value next-array)
                                         (push value all))
                                (setf current-array (reverse next-array)))
                    finally (return (nreverse (delete-if #'(lambda (el) (< (length el) x)) all)))))
            

            还有另一个版本,稍短一些,并使用更多循环功能:

            (defun perms (x y original-string)
              (loop repeat y
                    collect (loop for string in (or (car (last sets)) (list ""))
                                  append (loop for c across original-string
                                               collect (concatenate 'string string (string c)))) into sets
                    finally (return (loop for set in sets
                                          append (loop for el in set when (>= (length el) x) collect el)))))
            

            【讨论】:

              【解决方案11】:

              这里有一个简单的C#递归解法:

              方法:

              public ArrayList CalculateWordPermutations(string[] letters, ArrayList words, int index)
                      {
                          bool finished = true;
                          ArrayList newWords = new ArrayList();
                          if (words.Count == 0)
                          {
                              foreach (string letter in letters)
                              {
                                  words.Add(letter);
                              }
                          }
              
                          for(int j=index; j<words.Count; j++)
                          {
                              string word = (string)words[j];
                              for(int i =0; i<letters.Length; i++)
                              {
                                  if(!word.Contains(letters[i]))
                                  {
                                      finished = false;
                                      string newWord = (string)word.Clone();
                                      newWord += letters[i];
                                      newWords.Add(newWord);
                                  }
                              }
                          }
              
                          foreach (string newWord in newWords)
                          {   
                              words.Add(newWord);
                          }
              
                          if(finished  == false)
                          {
                              CalculateWordPermutations(letters, words, words.Count - newWords.Count);
                          }
                          return words;
                      }
              

              来电:

              string[] letters = new string[]{"a","b","c"};
              ArrayList words = CalculateWordPermutations(letters, new ArrayList(), 0);
              

              【讨论】:

                【解决方案12】:

                ...这里是 C 版本:

                void permute(const char *s, char *out, int *used, int len, int lev)
                {
                    if (len == lev) {
                        out[lev] = '\0';
                        puts(out);
                        return;
                    }
                
                    int i;
                    for (i = 0; i < len; ++i) {
                        if (! used[i])
                            continue;
                
                        used[i] = 1;
                        out[lev] = s[i];
                        permute(s, out, used, len, lev + 1);
                        used[i] = 0;
                    }
                    return;
                }
                

                【讨论】:

                  【解决方案13】:

                  permute (ABC) -> A.perm(BC) -> A.perm[B.perm(C)] -> A.perm[(*BC), (CB*)] -> [(*ABC), (BA C), (BCA*), (*ACB), (CAB), (CBA*)] 要在插入每个字母表时删除重复项,请检查前一个字符串是否以相同的字母表结尾(为什么?-练习)

                  public static void main(String[] args) {
                  
                      for (String str : permStr("ABBB")){
                          System.out.println(str);
                      }
                  }
                  
                  static Vector<String> permStr(String str){
                  
                      if (str.length() == 1){
                          Vector<String> ret = new Vector<String>();
                          ret.add(str);
                          return ret;
                      }
                  
                      char start = str.charAt(0);
                      Vector<String> endStrs = permStr(str.substring(1));
                      Vector<String> newEndStrs = new Vector<String>();
                      for (String endStr : endStrs){
                          for (int j = 0; j <= endStr.length(); j++){
                              if (endStr.substring(0, j).endsWith(String.valueOf(start)))
                                  break;
                              newEndStrs.add(endStr.substring(0, j) + String.valueOf(start) + endStr.substring(j));
                          }
                      }
                      return newEndStrs;
                  }
                  

                  打印所有没有重复的排列

                  【讨论】:

                    【解决方案14】:

                    C++ 中的递归解决方案

                    int main (int argc, char * const argv[]) {
                            string s = "sarp";
                            bool used [4];
                            permute(0, "", used, s);
                    }
                    
                    void permute(int level, string permuted, bool used [], string &original) {
                        int length = original.length();
                    
                        if(level == length) { // permutation complete, display
                            cout << permuted << endl;
                        } else {
                            for(int i=0; i<length; i++) { // try to add an unused character
                                if(!used[i]) {
                                    used[i] = true;
                                    permute(level+1, original[i] + permuted, used, original); // find the permutations starting with this string
                                    used[i] = false;
                                }
                            }
                    }
                    

                    【讨论】:

                      【解决方案15】:

                      在 Perl 中,如果你想限制自己使用小写字母,你可以这样做:

                      my @result = ("a" .. "zzzz");
                      

                      这给出了使用小写字符的 1 到 4 个字符之间的所有可能字符串。对于大写,将"a" 更改为"A" 并将"zzzz" 更改为"ZZZZ"

                      对于混合大小写,它变得更加困难,并且可能无法使用 Perl 的内置运算符之一来实现。

                      【讨论】:

                        【解决方案16】:

                        有效的 Ruby 答案:

                        class String
                          def each_char_with_index
                            0.upto(size - 1) do |index|
                              yield(self[index..index], index)
                            end
                          end
                          def remove_char_at(index)
                            return self[1..-1] if index == 0
                            self[0..(index-1)] + self[(index+1)..-1]
                          end
                        end
                        
                        def permute(str, prefix = '')
                          if str.size == 0
                            puts prefix
                            return
                          end
                          str.each_char_with_index do |char, index|
                            permute(str.remove_char_at(index), prefix + char)
                          end
                        end
                        
                        # example
                        # permute("abc")
                        

                        【讨论】:

                        【解决方案17】:

                        以下 Java 递归打印给定字符串的所有排列:

                        //call it as permut("",str);
                        
                        public void permut(String str1,String str2){
                            if(str2.length() != 0){
                                char ch = str2.charAt(0);
                                for(int i = 0; i <= str1.length();i++)
                                    permut(str1.substring(0,i) + ch + str1.substring(i,str1.length()),
                                             str2.substring(1,str2.length()));
                            }else{
                            System.out.println(str1);
                            }
                        }
                        

                        以下是上述“permut”方法的更新版本,它使 n! (n 阶乘) 与上述方法相比,递归调用更少

                        //call it as permut("",str);
                        
                        public void permut(String str1,String str2){
                           if(str2.length() > 1){
                               char ch = str2.charAt(0);
                               for(int i = 0; i <= str1.length();i++)
                                  permut(str1.substring(0,i) + ch + str1.substring(i,str1.length()),
                                         str2.substring(1,str2.length()));
                           }else{
                            char ch = str2.charAt(0);
                            for(int i = 0; i <= str1.length();i++)
                                System.out.println(str1.substring(0,i) + ch +    str1.substring(i,str1.length()),
                                         str2.substring(1,str2.length()));
                           }
                        }
                        

                        【讨论】:

                        • 这是最干净的解决方案,相信我之前在《Cracking the Coding Interview》一书中见过
                        • @TaoZhang 感谢您的补充,我没有从任何地方复制它,但是可能有人创建了类似的算法。无论如何,我已经更新了上面的代码以减少递归调用
                        【解决方案18】:
                        import java.util.*;
                        
                        public class all_subsets {
                            public static void main(String[] args) {
                                String a = "abcd";
                                for(String s: all_perm(a)) {
                                    System.out.println(s);
                                }
                            }
                        
                            public static Set<String> concat(String c, Set<String> lst) {
                                HashSet<String> ret_set = new HashSet<String>();
                                for(String s: lst) {
                                    ret_set.add(c+s);
                                }
                                return ret_set;
                            }
                        
                            public static HashSet<String> all_perm(String a) {
                                HashSet<String> set = new HashSet<String>();
                                if(a.length() == 1) {
                                    set.add(a);
                                } else {
                                    for(int i=0; i<a.length(); i++) {
                                        set.addAll(concat(a.charAt(i)+"", all_perm(a.substring(0, i)+a.substring(i+1, a.length()))));
                                    }
                                }
                                return set;
                            }
                        }
                        

                        【讨论】:

                          【解决方案19】:

                          我不知道你为什么要首先这样做。任何中等大小的 x 和 y 值的结果集都将是巨大的,并且会随着 x 和/或 y 变大而呈指数增长。

                          假设您的可能字符集是字母表中的 26 个小写字母,并且您要求您的应用程序生成长度 = 5 的所有排列。假设您没有耗尽内存,您将获得 11,881,376(即 26 5) 的幂次方。将该长度增加到 6,您将获得 308,915,776 个字符串。这些数字很快就会变得非常大。

                          这是我用 Java 编写的一个解决方案。您需要提供两个运行时参数(对应于 x 和 y)。玩得开心。

                          public class GeneratePermutations {
                              public static void main(String[] args) {
                                  int lower = Integer.parseInt(args[0]);
                                  int upper = Integer.parseInt(args[1]);
                          
                                  if (upper < lower || upper == 0 || lower == 0) {
                                      System.exit(0);
                                  }
                          
                                  for (int length = lower; length <= upper; length++) {
                                      generate(length, "");
                                  }
                              }
                          
                              private static void generate(int length, String partial) {
                                  if (length <= 0) {
                                      System.out.println(partial);
                                  } else {
                                      for (char c = 'a'; c <= 'z'; c++) {
                                          generate(length - 1, partial + c);
                                      }
                                  }
                              }
                          }
                          

                          【讨论】:

                          • 很长一段时间,但您不是通过重复生成它们吗?
                          【解决方案20】:

                          这是我想出的一个非递归版本,用 javascript 编写。 它不是基于上面 Knuth 的非递归的,尽管它在元素交换方面有一些相似之处。 我已经验证了它对最多 8 个元素的输入数组的正确性。

                          一个快速的优化将是预飞行out 数组并避免push()

                          基本思路是:

                          1. 给定一个源数组,生成第一组新数组,依次将第一个元素与每个后续元素交换,每次都保持其他元素不受干扰。 eg:给定1234,生成1234、2134、3214、4231。

                          2. 使用前一次遍历中的每个数组作为新遍历的种子, 但不是交换第一个元素,而是将第二个元素与每个后续元素交换。另外,这次不要在输出中包含原始数组。

                          3. 重复步骤 2 直到完成。

                          这是代码示例:

                          function oxe_perm(src, depth, index)
                          {
                              var perm = src.slice();     // duplicates src.
                              perm = perm.split("");
                              perm[depth] = src[index];
                              perm[index] = src[depth];
                              perm = perm.join("");
                              return perm;
                          }
                          
                          function oxe_permutations(src)
                          {
                              out = new Array();
                          
                              out.push(src);
                          
                              for (depth = 0; depth < src.length; depth++) {
                                  var numInPreviousPass = out.length;
                                  for (var m = 0; m < numInPreviousPass; ++m) {
                                      for (var n = depth + 1; n < src.length; ++n) {
                                          out.push(oxe_perm(out[m], depth, n));
                                      }
                                  }
                              }
                          
                              return out;
                          }
                          

                          【讨论】:

                            【解决方案21】:

                            在红宝石中:

                            str = "a"
                            100_000_000.times {puts str.next!}
                            

                            速度很快,但需要一些时间 =)。当然,如果你对短字符串不感兴趣,你可以从“aaaaaaaa”开始。

                            虽然我可能误解了实际问题 - 在其中一篇文章中,听起来好像您只需要一个蛮力的字符串库,但在主要问题中,您似乎需要置换一个特定的字符串。

                            你的问题有点类似于这个问题:http://beust.com/weblog/archives/000491.html(列出所有没有数字重复的整数,这导致很多语言都可以解决它,ocaml 的家伙使用排列,还有一些 java 的家伙使用另一种解决方案)。

                            【讨论】:

                            • 你的提案的一个问题是str.next!不会遍历所有可打印字符。您的示例只会生成小写字母 - 没有标点符号或大写字母。
                            【解决方案22】:

                            我今天需要这个,尽管已经给出的答案为我指明了正确的方向,但它们并不是我想要的。

                            这是一个使用 Heap 方法的实现。数组的长度必须至少为 3 并且出于实际考虑不大于 10 左右,这取决于您要做什么,耐心和时钟速度。

                            在进入循环之前,使用第一个排列初始化 Perm(1 To N),使用零* 初始化 Stack(3 To N),使用 2** 初始化 Level。在循环结束时调用NextPerm,完成后返回false。

                            * VB 会为你做到这一点。

                            ** 您可以稍微更改 NextPerm 以使其不必要,但这样更清晰。

                            Option Explicit
                            
                            Function NextPerm(Perm() As Long, Stack() As Long, Level As Long) As Boolean
                            Dim N As Long
                            If Level = 2 Then
                                Swap Perm(1), Perm(2)
                                Level = 3
                            Else
                                While Stack(Level) = Level - 1
                                    Stack(Level) = 0
                                    If Level = UBound(Stack) Then Exit Function
                                    Level = Level + 1
                                Wend
                                Stack(Level) = Stack(Level) + 1
                                If Level And 1 Then N = 1 Else N = Stack(Level)
                                Swap Perm(N), Perm(Level)
                                Level = 2
                            End If
                            NextPerm = True
                            End Function
                            
                            Sub Swap(A As Long, B As Long)
                            A = A Xor B
                            B = A Xor B
                            A = A Xor B
                            End Sub
                            
                            'This is just for testing.
                            Private Sub Form_Paint()
                            Const Max = 8
                            Dim A(1 To Max) As Long, I As Long
                            Dim S(3 To Max) As Long, J As Long
                            Dim Test As New Collection, T As String
                            For I = 1 To UBound(A)
                                A(I) = I
                            Next
                            Cls
                            ScaleLeft = 0
                            J = 2
                            Do
                                If CurrentY + TextHeight("0") > ScaleHeight Then
                                    ScaleLeft = ScaleLeft - TextWidth(" 0 ") * (UBound(A) + 1)
                                    CurrentY = 0
                                    CurrentX = 0
                                End If
                                T = vbNullString
                                For I = 1 To UBound(A)
                                    Print A(I);
                                    T = T & Hex(A(I))
                                Next
                                Print
                                Test.Add Null, T
                            Loop While NextPerm(A, S, J)
                            J = 1
                            For I = 2 To UBound(A)
                                J = J * I
                            Next
                            If J <> Test.Count Then Stop
                            End Sub
                            

                            其他方法由不同的作者描述。 Knuth 描述了两种,一种给出词汇顺序,但复杂而缓慢,另一种被称为平易近人的方法。高杰和王殿军也写了一篇有趣的论文。

                            【讨论】:

                              【解决方案23】:

                              这是一个描述如何打印字符串排列的链接。 http://nipun-linuxtips.blogspot.in/2012/11/print-all-permutations-of-characters-in.html

                              【讨论】:

                                【解决方案24】:

                                python 中的这段代码在调用时将allowed_characters 设置为[0,1] 和最多 4 个字符,将生成 2^4 个结果:

                                ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111']

                                def generate_permutations(chars = 4) :
                                
                                #modify if in need!
                                    allowed_chars = [
                                        '0',
                                        '1',
                                    ]
                                
                                    status = []
                                    for tmp in range(chars) :
                                        status.append(0)
                                
                                    last_char = len(allowed_chars)
                                
                                    rows = []
                                    for x in xrange(last_char ** chars) :
                                        rows.append("")
                                        for y in range(chars - 1 , -1, -1) :
                                            key = status[y]
                                            rows[x] = allowed_chars[key] + rows[x]
                                
                                        for pos in range(chars - 1, -1, -1) :
                                            if(status[pos] == last_char - 1) :
                                                status[pos] = 0
                                            else :
                                                status[pos] += 1
                                                break;
                                
                                    return rows
                                
                                import sys
                                
                                
                                print generate_permutations()
                                

                                希望这对你有用。适用于任何字符,不仅是数字

                                【讨论】:

                                • 这不是排列,而是子集选择,即 ABC & 001 = C,而有效排列必须包含所有三个字符。
                                • 呃?对不起,我不明白你在说什么。如果你修复它留下一个固定的版本,我会社区 wiki 的东西
                                【解决方案25】:

                                之前的许多答案都使用了回溯。这是在初始排序后生成排列的 O(n*n!) 渐近最优方式

                                class Permutation {
                                
                                    /* runtime -O(n) for generating nextPermutaion
                                     * and O(n*n!) for generating all n! permutations with increasing sorted array as start
                                     * return true, if there exists next lexicographical sequence
                                     * e.g [a,b,c],3-> true, modifies array to [a,c,b]
                                     * e.g [c,b,a],3-> false, as it is largest lexicographic possible */
                                    public static boolean nextPermutation(char[] seq, int len) {
                                        // 1
                                        if (len <= 1)
                                            return false;// no more perm
                                        // 2: Find last j such that seq[j] <= seq[j+1]. Terminate if no such j exists
                                        int j = len - 2;
                                        while (j >= 0 && seq[j] >= seq[j + 1]) {
                                            --j;
                                        }
                                        if (j == -1)
                                            return false;// no more perm
                                        // 3: Find last l such that seq[j] <= seq[l], then exchange elements j and l
                                        int l = len - 1;
                                        while (seq[j] >= seq[l]) {
                                            --l;
                                        }
                                        swap(seq, j, l);
                                        // 4: Reverse elements j+1 ... count-1:
                                        reverseSubArray(seq, j + 1, len - 1);
                                        // return seq, add store next perm
                                
                                        return true;
                                    }
                                    private static void swap(char[] a, int i, int j) {
                                        char temp = a[i];
                                        a[i] = a[j];
                                        a[j] = temp;
                                    }
                                
                                    private static void reverseSubArray(char[] a, int lo, int hi) {
                                        while (lo < hi) {
                                            swap(a, lo, hi);
                                            ++lo;
                                            --hi;
                                        }
                                    }
                                    public static void main(String[] args) {
                                        String str = "abcdefg";
                                        char[] array = str.toCharArray();
                                        Arrays.sort(array);
                                        int cnt=0;
                                        do {
                                            System.out.println(new String(array));
                                            cnt++;
                                        }while(nextPermutation(array, array.length));
                                        System.out.println(cnt);//5040=7!
                                    }
                                    //if we use "bab"-> "abb", "bab", "bba", 3(#permutations)
                                }
                                

                                【讨论】:

                                  【解决方案26】:

                                  递归方法

                                  func StringPermutations(inputStr string) (permutations []string) {
                                      for i := 0; i < len(inputStr); i++ {
                                          inputStr = inputStr[1:] + inputStr[0:1]
                                          if len(inputStr) <= 2 {
                                              permutations = append(permutations, inputStr)
                                              continue
                                          }
                                          leftPermutations := StringPermutations(inputStr[0 : len(inputStr)-1])
                                          for _, leftPermutation := range leftPermutations {
                                              permutations = append(permutations, leftPermutation+inputStr[len(inputStr)-1:])
                                          }
                                      }
                                      return
                                  }
                                  

                                  【讨论】:

                                    【解决方案27】:

                                    虽然这不能准确回答您的问题,但这里有一种方法可以从多个相同长度的字符串中生成字母的每个排列:例如,如果您的单词是“coffee”、“joomla”和“moodle” ,您可以期待“coodle”、“joodee”、“joffle”等输出。

                                    基本上,组合的数量是(单词数)的(每个单词的字母数)的幂。因此,在 0 和组合数 - 1 之间选择一个随机数,将该数字转换为基数(单词数),然后使用该数字的每个数字作为从哪个单词中获取下一个字母的指示符。

                                    例如:在上面的例子中。 3 个单词,6 个字母 = 729 种组合。选择一个随机数:465。转换为基数 3:122020。从单词 1 中取出第一个字母,从单词 2 中取出第 2 个字母,从单词 2 中取出第 3 个字母,从单词 0 中取出第 4 个字母……你会得到……“joofle”。

                                    如果你想要所有的排列,只需从 0 到 728 循环。当然,如果你只是选择一个随机值,一个更更简单不太混淆的方法是循环字母。如果您需要所有排列,此方法可让您避免递归,而且它使您看起来像您知道数学(tm)

                                    如果组合的数量过多,你可以把它分解成一系列更小的单词,并在最后连接起来。

                                    【讨论】:

                                      【解决方案28】:

                                      c# 迭代:

                                      public List<string> Permutations(char[] chars)
                                          {
                                              List<string> words = new List<string>();
                                              words.Add(chars[0].ToString());
                                              for (int i = 1; i < chars.Length; ++i)
                                              {
                                                  int currLen = words.Count;
                                                  for (int j = 0; j < currLen; ++j)
                                                  {
                                                      var w = words[j];
                                                      for (int k = 0; k <= w.Length; ++k)
                                                      {
                                                          var nstr = w.Insert(k, chars[i].ToString());
                                                          if (k == 0)
                                                              words[j] = nstr;
                                                          else
                                                              words.Add(nstr);
                                                      }
                                                  }
                                              }
                                              return words;
                                          }
                                      

                                      【讨论】:

                                        【解决方案29】:
                                        def gen( x,y,list): #to generate all strings inserting y at different positions
                                        list = []
                                        list.append( y+x )
                                        for i in range( len(x) ):
                                            list.append( func(x,0,i) + y + func(x,i+1,len(x)-1) )
                                        return list 
                                        
                                        def func( x,i,j ): #returns x[i..j]
                                        z = '' 
                                        for i in range(i,j+1):
                                            z = z+x[i]
                                        return z 
                                        
                                        def perm( x , length , list ): #perm function
                                        if length == 1 : # base case
                                            list.append( x[len(x)-1] )
                                            return list 
                                        else:
                                            lists = perm( x , length-1 ,list )
                                            lists_temp = lists #temporarily storing the list 
                                            lists = []
                                            for i in range( len(lists_temp) ) :
                                                list_temp = gen(lists_temp[i],x[length-2],lists)
                                                lists += list_temp 
                                            return lists
                                        

                                        【讨论】:

                                          【解决方案30】:
                                          def permutation(str)
                                            posibilities = []
                                            str.split('').each do |char|
                                              if posibilities.size == 0
                                                posibilities[0] = char.downcase
                                                posibilities[1] = char.upcase
                                              else
                                                posibilities_count = posibilities.length
                                                posibilities = posibilities + posibilities
                                                posibilities_count.times do |i|
                                                  posibilities[i] += char.downcase
                                                  posibilities[i+posibilities_count] += char.upcase
                                                end
                                              end
                                            end
                                            posibilities
                                          end
                                          

                                          这是我对非递归版本的看法

                                          【讨论】:

                                            猜你喜欢
                                            • 1970-01-01
                                            • 2023-03-04
                                            • 2019-07-18
                                            • 2012-05-05
                                            • 2011-05-13
                                            • 1970-01-01
                                            • 2015-06-18
                                            • 2019-05-19
                                            相关资源
                                            最近更新 更多