查找带有和不带连字符的所有可能的单词组合

时间:2016-03-24 03:56:33

标签: c# math combinatorics

对于可能包含零个或多个连字符的字符串,我需要使用和不连字符提取所有不同的可能性。

例如,字符串" A-B"会导致" A-B"和" AB" (两种可能性)。

字符串" A-B-C"会导致" A-B-C"," AB-C"," A-BC"和" ABC" (四种可能性)。

字符串" A-B-C-D"将导致" ABC-D"," AB-C-D"," A-BC-D"," AB-CD" ," AB-CD"," ABC-D"," A-BCD"和" ABCD" (八种可能性)。

......等等。

我已尝试过一些嵌套循环,但无法获得所需的结果。我怀疑我需要一些递归的东西,除非有一些我忽略的简单解决方案。

NB。这是为了构建一个SQL查询(遗憾的是SQL Server没有MySQL的REGEXP模式匹配)。

这是我正在努力的一次尝试。如果我以递归方式执行此操作,这可能会有效。

string keyword = "A-B-C-D";

List<int> hyphens = new List<int>();

int pos = keyword.IndexOf('-');
while (pos != -1)
{
    hyphens.Add(pos);
    pos = keyword.IndexOf('-', pos + 1);
}

for (int i = 0; i < hyphens.Count(); i++)
{
    string result = keyword.Substring(0, hyphens[i]) + keyword.Substring(hyphens[i] + 1);

    Response.Write("<p>" + result);
}

A B C D是不同长度的词。

5 个答案:

答案 0 :(得分:7)

查看您的示例案例。你注意到了一种模式吗?

  • 有1个连字符,有2种可能性。
  • 有2个连字符,有4种可能性。
  • 有3个连字符,有8种可能性。

可能性的数量是2 n

这实际上是指数式增长,因此如果字符串中有过多的连字符,那么将它们全部打印将很快变得不可行。 (只有30个连字符,有超过十亿种组合!)

也就是说,对于较少数量的连字符,生成列表可能会很有趣。为此,您可以将每个连字符视为二进制数中的一个位。如果该位为1,则连字符存在,否则不存在。所以这表明了一个相当简单的解决方案:

  1. 拆分连字符上的原始字符串
  2. 设n =连字符数
  3. 从2 n - 1到0计数。将此计数器视为位掩码。
  4. 对于每个计数,开始构建从第一部分开始的字符串。
  5. 按顺序将每个剩余部分连接到字符串,只有在设置了位掩码中的相应位时才以连字符开头。
  6. 将结果字符串添加到输出中并继续,直到计数器耗尽。
  7. 翻译成我们的代码:

    public static IEnumerable<string> EnumerateHyphenatedStrings(string s)
    {
        string[] parts = s.Split('-');
        int n = parts.Length - 1;
        if (n > 30) throw new Exception("too many hyphens");
        for (int m = (1 << n) - 1; m >= 0; m--)
        {
            StringBuilder sb = new StringBuilder(parts[0]);
            for (int i = 1; i <= n; i++)
            {
                if ((m & (1 << (i - 1))) > 0) sb.Append('-');
                sb.Append(parts[i]);
            }
            yield return sb.ToString();
        }
    }
    

    小提琴:https://dotnetfiddle.net/ne3N8f

答案 1 :(得分:5)

你应该能够跟踪每个连字符位置,并且基本上说它在那里或不在那里。循环遍历所有组合,你就得到了所有的字符串。我发现跟踪它的最简单方法是使用二进制文件,因为它很容易添加Convert.ToInt32

我想出了这个:

string keyword = "A-B-C-D";
string[] keywordSplit = keyword.Split('-');
int combinations = Convert.ToInt32(Math.Pow(2.0, keywordSplit.Length - 1.0));

List<string> results = new List<string>();

for (int j = 0; j < combinations; j++)
{
    string result = "";
    string hyphenAdded = Convert.ToString(j, 2).PadLeft(keywordSplit.Length - 1, '0');
    // Generate string
    for (int i = 0; i < keywordSplit.Length; i++)
    {
        result += keywordSplit[i] +
                  ((i < keywordSplit.Length - 1) && (hyphenAdded[i].Equals('1')) ? "-" : "");
    }
    results.Add(result);
}

答案 2 :(得分:3)

这对我有用:

Func<IEnumerable<string>, IEnumerable<string>> expand = null;
expand = xs =>
{
    if (xs != null && xs.Any())
    {
        var head = xs.First();
        if (xs.Skip(1).Any())
        {
            return expand(xs.Skip(1)).SelectMany(tail => new []
            {
                head + tail,
                head + "-" + tail
            });
        }
        else
        {
            return new [] { head };
        }
    }
    else
    {
        return Enumerable.Empty<string>();
    }
};

var keyword = "A-B-C-D";

var parts = keyword.Split('-');

var results = expand(parts);

我明白了:

ABCD 
A-BCD 
AB-CD 
A-B-CD 
ABC-D 
A-BC-D 
AB-C-D 
A-B-C-D 

答案 3 :(得分:1)

我不确定你的问题是否完全明确(​​即你能不能使用A-BCD-EF-G-H?)。对于&#34;完全&#34;连字符串(A-B-C-D -...- Z),这样的事情应该这样做:

string toParse = "A-B-C-D";
char[] toParseChars = toPase.toCharArray();
string result = "";
string binary;
for(int i = 0; i < (int)Math.pow(2, toParse.Length/2); i++) { // Number of subsets of an n-elt set is 2^n
    binary = Convert.ToString(i, 2);
    while (binary.Length < toParse.Length/2) {
        binary = "0" + binary;
    }
    char[] binChars = binary.ToCharArray();

    for (int k = 0; k < binChars.Length; k++) {
        result += toParseChars[k*2].ToString();
        if (binChars[k] == '1') {
            result += "-";
        }
    }
    result += toParseChars[toParseChars.Length-1];
    Console.WriteLine(result);
}

这里的想法是我们想为每个可能的连字符创建一个二进制字。因此,如果我们有ABCD(三个连字符),我们创建二进制字000,001,010,011,100,101,110和111.注意,如果我们有n个连字符,我们需要2 ^ n个二进制字。

然后,每个单词通过插入连字符映射到您想要的输出,其中我们有一个&#39; 1&#39;在我们的文字中(000-> ABCD,001-> ABC-D,010-> AB-CD等)。我没有测试上面的代码,但这至少是解决完全连字的问题的一种方法。

免责声明:我没有真正测试代码

答案 4 :(得分:1)

我已经测试了这段代码,它正如问题中指定的那样工作。我将字符串存储在List<string>

string str = "AB-C-D-EF-G-HI";
string[] splitted = str.Split('-');

List<string> finalList = new List<string>();

string temp = "";

for (int i = 0; i < splitted.Length; i++)
{
    temp += splitted[i];
}
finalList.Add(temp);
temp = "";

for (int diff = 0; diff < splitted.Length-1; diff++)
{
    for (int start = 1, limit = start + diff; limit < splitted.Length; start++, limit++)
    {
        int i = 0;
        while (i < start)
        {
            temp += splitted[i++];
        }
        while (i <= limit)
        {
            temp += "-";
            temp += splitted[i++];
        }
        while (i < splitted.Length)
        {
            temp += splitted[i++];
        }
        finalList.Add(temp);
        temp = "";
    }
}