【问题标题】:String Combinations With Character Replacement带字符替换的字符串组合
【发布时间】:2025-12-12 23:05:01
【问题描述】:

我正在尝试解决我以前从未见过的场景,并且正在努力想出一个算法来正确地实现它。我的部分问题是对正确术语的模糊回忆。我相信我需要的是标准“组合”问题的变体,但我很可能会离开那里。

情景 给定一个示例字符串"100"(我们称之为x),生成x 的所有组合,将0(零)字符之一换成o(小写的o)。因此,对于"100" 的简单示例,我希望得到以下输出:

  • "100"
  • "10o"
  • "1o0"
  • "1oo"

这需要支持具有不同数量的0 字符的不同长度的字符串,但假设0 的实例永远不会超过5 个。

我有一个非常简单的算法,适用于我的 "100" 样本,但对于任何更长/更复杂的情况都会崩溃:

public IEnumerable<string> Combinations(string input)
{
    char[] buffer = new char[input.Length];

    for(int i = 0; i != buffer.Length; ++i)
    {
        buffer[i] = input[i];
    }

    //return the original input
    yield return new string(buffer);

    //look for 0's and replace them
    for(int i = 0; i != buffer.Length; ++i)
    {
        if (input[i] == '0')
        {
            buffer[i] = 'o';
            yield return new string(buffer);
            buffer[i] = '0';
        }
    }

    //handle the replace-all scenario
    yield return input.Replace("0", "o");
}

我有一种挥之不去的感觉,递归可能是我的朋友,但我正在努力弄清楚如何在这里合并我需要的条件逻辑。

【问题讨论】:

  • 难道你不能只拥有一个零位置的本地数组,然后以二进制模式枚举替换,零和小 o 作为二进制数字吗?
  • @MOehm 不确定我是否理解你的意思,你能提供一个实现和/或其他细节吗?

标签: c# algorithm combinatorics


【解决方案1】:

你的猜测是正确的;递归是您应对这一挑战的朋友。这是一个简单的解决方案:

public static IEnumerable<string> Combinations(string input)
{
    int firstZero = input.IndexOf('0');   // Get index of first '0'
    if (firstZero == -1)      // Base case: no further combinations
        return new string[] { input };

    string prefix = input.Substring(0, firstZero);    // Substring preceding '0'
    string suffix = input.Substring(firstZero + 1);   // Substring succeeding '0'
    // e.g. Suppose input was "fr0d00"
    //      Prefix is "fr"; suffix is "d00"

    // Recursion: Generate all combinations of suffix
    // e.g. "d00", "d0o", "do0", "doo"
    var recursiveCombinations = Combinations(suffix);

    // Return sequence in which each string is a concatenation of the
    // prefix, either '0' or 'o', and one of the recursively-found suffixes
    return 
        from chr in "0o"  // char sequence equivalent to: new [] { '0', 'o' }
        from recSuffix in recursiveCombinations
        select prefix + chr + recSuffix;                                    
}

【讨论】:

  • 多么好的解决方案! +1
  • @Enigmativity:谢谢!你的也很直观和功能风格(+1)
【解决方案2】:

这对我有用:

public IEnumerable<string> Combinations(string input)
{
    var head = input[0] == '0' //Do I have a `0`?
        ? new [] { "0", "o" } //If so output both `"0"` & `"o"`
        : new [] { input[0].ToString() }; //Otherwise output the current character

    var tails = input.Length > 1 //Is there any more string?
        ? Combinations(input.Substring(1)) //Yes, recursively compute
        : new[] { "" }; //Otherwise, output empty string

    //Now, join it up and return
    return
        from h in head
        from t in tails
        select h + t;
}

【讨论】:

    【解决方案3】:

    您在这里不需要递归,您可以枚举您的模式并将它们视为二进制数。例如,如果你的字符串中有三个零,你会得到:

    0    000    ....0..0....0...
    1    001    ....0..0....o...
    2    010    ....0..o....0...
    3    011    ....0..o....o...
    4    100    ....o..0....0...
    5    101    ....o..0....o...
    6    110    ....o..o....0...
    7    111    ....o..o....o...
    

    您可以使用按位运算符或将要替换的字符视为里程表来实现。

    下面是 C 中的一个实现。我不熟悉 C#,从其他答案中我看到 C# 已经有合适的标准类来实现你想要的。 (虽然我很惊讶有这么多人在这里提出递归。)

    因此,这更像是对我对该问题的评论的解释或说明,而不是针对您的问题的实施建议。

    int binrep(char str[])
    {
        int zero[40];       // indices of zeros
        int nzero = 0;      // number of zeros in string
        int ncombo = 1;     // number of result strings
        int i, j;
    
        for (i = 0; str[i]; i++) {
            if (str[i] == '0') {
                zero[nzero++] = i;
                ncombo <<= 1;
            }
        }
    
        for (i = 0; i < ncombo; i++) {
            for (j = 0; j < nzero; j++) {
                str[zero[j]] = ((i >> j) & 1) ? 'o' : '0';
            }
    
            printf("%s\n", str);    // should yield here
        }
    
        return ncombo;
    }
    

    【讨论】:

    • 感谢您抽出宝贵时间发布此内容,我很高兴您分享了一种不同的方法。
    【解决方案4】:

    这是一个使用递归的解决方案,以及您的缓冲区数组:

    private static void Main(string[] args)
    {
        var a = Combinations("100");
        var b = Combinations("10000");
    }
    
    public static IEnumerable<string> Combinations(string input)
    {
        var combinations = new List<string>();
    
        combinations.Add(input);
    
        for (int i = 0; i < input.Length; i++)
        {
            char[] buffer= input.ToArray();
            if (buffer[i] == '0')
            {
                buffer[i] = 'o';
                combinations.Add(new string(buffer));
                combinations = combinations.Concat(Combinations(new string(buffer))).ToList();
            }
        }
    
        return combinations.Distinct();
    }
    

    该方法将原始输入添加为第一个结果。之后,我们在循环中替换 0s 我们看到的 o 并使用新输入调用我们的方法,这将涵盖多个 0s 的情况。

    最后,我们得到了几个重复,所以我们使用Distinct

    【讨论】:

    • 很好,感谢您同情我并将我的原始实现融入您的实现中。
    【解决方案5】:

    我知道较早的答案更好。但我不希望我的代码浪费掉。 :)

    我解决这个组合问题的方法是利用二进制数的工作原理。我的算法如下:

    List<string> ZeroCombiner(string str)
    {
        // Get number of zeros.
        var n = str.Count(c => c == '0');
        var limit = (int)Math.Pow(2, n);
    
        // Create strings of '0' and 'o' based on binary numbers from 0 to 2^n.
        var binaryStrings = new List<string>();
        for (int i = 0; i < limit; ++i )
        {
            binaryStrings.Add(Binary(i, n + 1));
        }
    
        // Replace each zero with respect to each binary string.
        var result = new List<string>();
        foreach (var binaryString in binaryStrings)
        {
            var zeroCounter = 0;
            var combinedString = string.Empty;
            for (int i = 0; i < str.Length; ++i )
            {
                if (str[i] == '0')
                {
                    combinedString += binaryString[zeroCounter];
                    ++zeroCounter;
                }
                else
                    combinedString += str[i];
            }
            result.Add(combinedString);
        }
    
        return result;
    }
    
    string Binary(int i, int n)
    {
        string result = string.Empty;
        while (n != 0)
        {
            result = result + (i % 2 == 0 ? '0' : 'o');
            i = i / 2;
            --n;
        }
        return result;
    }
    

    【讨论】: