【问题标题】:Generating all variations of certain length out of string从字符串中生成一定长度的所有变体
【发布时间】:2017-06-03 23:34:59
【问题描述】:

我见过很少的 C# 中字符串变体的实现,但它们都没有对它们的长度有任何限制。不幸的是,我无法修改它们以实现我的目标,例如

为:

string = "ABCD" and variationLength = 2

生成新字符串:

AB, AC, AD, BA, BC, BD, CA, CB, CD, DA, DB, DC 

我正在寻找这个 Python 的 itertools.permutations 实现,但在 C# 中。 (https://docs.python.org/3/library/itertools.html#itertools.permutations)

在 C# 中是否有类似的东西?如果没有,那么最简单的实现方法是什么?

编辑_2: 到目前为止,我想出了一个想法,列出给定字符串的所有唯一字符,然后从中获取变体

static void PrintAllKLengthPerm(string str, int k)
{
    int n = str.Length;
    PrintAllKLengthPermRec(str, "", n, k);
}

// The main recursive method to print all possible strings of length k
static void PrintAllKLengthPermRec(string str, String prefix, int n, int k)
{
    // Base case: k is 0, print prefix
    if (k == 0)
    {
        Console.WriteLine(prefix);
        return;
    }

    // One by one add all characters from str and recursively 
    // call for k equals to k-1
    for (int i = 0; i < n; ++i)
    {
        // Next character of input added
        String newPrefix = prefix + str[i];

        // k is decreased, because we have added a new character
        PrintAllKLengthPermRec(str, newPrefix, n, k - 1);
    }
}

static void Main(string[] args)
{
    string str = "ABCD";
    int permLen = 2;

    //get all unique characters in string
    string uniqStr = new String(str.Distinct().ToArray());

    // Print all possible strings of length permLen out of uniqStr characters
    PrintAllKLengthPerm(uniqStr, permLen);      
}

但是我正在寻找更优化和更有效的解决方案

【问题讨论】:

  • 请展示你的作品。到目前为止,您尝试过什么?
  • @Soviut 已编辑..
  • @JayDouble 这没有展示你的作品。实际尝试并显示代码,以便我们为您提供帮助。我了解您是新来的,所以我想澄清一下 SO 不是一个建议论坛,也不是一个为您编写代码的地方;我们帮助您解决代码问题。

标签: c# string permutation string-length


【解决方案1】:

这是一个真正的递归置换方法:

public IEnumerable<string> Permutate(string source, int count)
{
    if (source.Length == 1)
    {
        yield return source;
    }
    else if (count == 1)
    {
        for (var n = 0; n < source.Length; n++)
        {
            yield return source.Substring(n, 1);
        }
    }
    else
    {
        for (var n = 0; n < source.Length; n++)
            foreach (var suffix in Permutate(
                source.Substring(0, n)
                    + source.Substring(n + 1, source.Length - n - 1), count -1))
            {
                yield return source.Substring(n, 1) + suffix;
            }
    }
}

它可以用Permutate("ABCD", 2) 调用并返回:

【讨论】:

    【解决方案2】:

    我做了以下递归函数来完成你的任务:

    static void Permutations(List<string> output, string str, int n, string curr)
        {
            if(curr.Length == n)
            {
                output.Add(curr);
                return;
            }
            foreach(char c in str)
                if(!curr.Contains(c.ToString()))
                    Permutations(output, str, n, curr + c.ToString());
        }
    

    然后你这样称呼它:

    string str = "ABCD";
    int length = 2;
    List<string> perms = new List<string>();
    Permutations(perms, str, length, "");
    // now the list "perms" will contain the permutations of "str" in length "n"
    

    【讨论】:

      【解决方案3】:
      List<string> newPermutations = new List<string>();
      for(int a = 0; a!=inString.Count; a++)
          for((int b = 0; b!=inString.Count; b++)
              if(noRepetitions && a == b) continue;
              newPermutations.Add(""+inString[a] + inString[b]);
      

      我认为这应该可行;我仍在尝试找出一种不仅有 2 个字母的方法。

      编辑:编辑它工作,旧的只是没有工作......哈哈 编辑:感谢@Bloopy,他们帮助我发现了我的 for 循环中的一些错误

      【讨论】:

      • 要拥有一个可以更改的permutationLength 变量,也许您可​​以考虑使用该编号创建一个数组。的尺寸,然后循环通过这些。不过有两点:如果您将foreach 更改为for 并使用整数ij(检查i != j),那么您的解决方案将适用于具有重复元素的字符串排列。此外,您需要将a 转换为字符串,因为字符上的a + b 会添加它们的整数值。
      • @Bloopy 1. 一个人怎么能选择多个维度...我以前想过,但我从来没有想到过(我也没有做过任何真正的研究。) 2 . i & j 方法是个好主意,3. ""+a+b 会起作用。感谢您的改进!
      • 我研究了更多,我意识到它会比我现有的答案更复杂。但是,我确实找到了一种使用 LINQ 的替代方法,我已经添加了它作为替代方法。顺便说一句,你忘记增加你的 for 变量并且你已经在你的代码中添加了 3 个编译错误!!
      【解决方案4】:

      这是一个使用模数和除法的解决方案。使用字母 ABCD 有 4² 个可能的长度为 2 的字符串。从 0 到 4²-1 对它们进行编号,并重复将每个数字除以 4。将得到的余数用作 ABCD 字符串的数组索引。

      这样做的好处是允许您在需要时保留带有重复元素(AA、BB、CC、DD)的字符串——只需跳过丢弃步骤。

      string alphabet = "ABCD";
      int length = 2;
      
      int[] indexes = new int[length];
      char[] item = new char[length];
      
      // loop through all possible strings for the given alphabet and length
      for (int i = 0; i < Math.Pow(alphabet.Length, length); i++) {
      
          int dividend = i;
          for (int j = length - 1; j >= 0; j--) {
              indexes[j] = dividend % alphabet.Length;
              dividend /= alphabet.Length;
          }
      
          // discard any that use the same alphabet element more than once
          if (indexes.Distinct().Count() < length)
              continue;
      
          for (int k = 0; k < length; k++) {
              item[k] = alphabet[indexes[k]];
          }
      
          Console.WriteLine(item);
      }
      

      或者,这里有一个使用 LINQ 的非常简短的解决方案。请注意,如果字符串中有重复元素,则此选项将无法正常工作(除非您要删除对Where 的调用并保留AA、BB 等)。我需要像在上面的方法中那样跟踪索引。

      IEnumerable<string> prm = alphabet.Select(c => c.ToString());
      for (int a = 1; a < length; a++)
          prm = prm.SelectMany(s => alphabet.Where(t => !s.Contains(t)), (x, y) => x + y);
      
      foreach (string s in prm)
          Console.WriteLine(s);
      

      【讨论】:

        猜你喜欢
        • 2012-02-28
        • 1970-01-01
        • 2023-03-26
        • 2019-05-10
        • 1970-01-01
        • 2016-02-07
        • 2021-02-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多