C#中给定集合的字母组合的单词

时间:2016-03-12 20:54:29

标签: c# arrays multidimensional-array combinations

我正在尝试创建一个方法,可以输出单词,其中每个字母可以是给定集合中的任何字母。例如:

First letter can be: A or B
Second letter can be AB or C
Third letter can be ABC or D
Fourth letter can be AB or C

等等。 我将输入数据存储为二维数组,如下所示:

AB
ABC
ABCD
ABC
ABC

它应该能够生成如下字样:

AAAAA
BAAAA
ABAAA
etc.

使用多维数组调用方法,如下所示:

void printAllCombinations(int[,] arr)
{
    // Method code.
}

我希望能够使用任何长度和高度的多维数组调用此方法。我已经尝试过使用for循环,但是在彼此之间有太多for循环并不是最好的解决方案。

我尝试过的东西:

private static List<string> combinations(string[][] twoDimStringArray)
        {
            // keep track of the size of each inner String array
            int[] sizeArray = new int[twoDimStringArray.Length];

            // keep track of the index of each inner String array which will be used
            // to make the next combination
            int[] counterArray = new int[twoDimStringArray.Length];

            // Discover the size of each inner array and populate sizeArray.
            // Also calculate the total number of combinations possible using the
            // inner String array sizes.
            int totalCombinationCount = 1;
            for (int i = 0; i < twoDimStringArray.Length; ++i)
            {
                sizeArray[i] = twoDimStringArray[i].Length;
                totalCombinationCount *= twoDimStringArray[i].Length;
            }

            // Store the combinations in a List of String objects
            List<String> combinationList = new List<string>(totalCombinationCount);

            StringBuilder sb;  // more efficient than String for concatenation

            for (int countdown = totalCombinationCount; countdown > 0; --countdown)
            {
                // Run through the inner arrays, grabbing the member from the index
                // specified by the counterArray for each inner array, and build a
                // combination string.
                sb = new StringBuilder();
                for (int i = 0; i < twoDimStringArray.Length; ++i)
                {
                    sb.Append(twoDimStringArray[i][counterArray[i]]);
                }
                combinationList.Add(sb.ToString());  // add new combination to list

                // Now we need to increment the counterArray so that the next
                // combination is taken on the next iteration of this loop.
                for (int incIndex = twoDimStringArray.Length - 1; incIndex >= 0; --incIndex)
                {
                    if (counterArray[incIndex] + 1 < sizeArray[incIndex])
                    {
                        ++counterArray[incIndex];
                        // None of the indices of higher significance need to be
                        // incremented, so jump out of this for loop at this point.
                        break;
                    }
                    // The index at this position is at its max value, so zero it
                    // and continue this loop to increment the index which is more
                    // significant than this one.
                    counterArray[incIndex] = 0;
                }
            }
            return combinationList;
        }

有效的方法是什么?

1 个答案:

答案 0 :(得分:1)

您需要找到cartesian product 所有字符列表。如comment中所述,这具有O(N^M)复杂度,其中 N =每组中的字母 M =设置计数。毋庸置疑,计算时间很快就会出现。

我写了一个泛型扩展方法,它使用带yield return的递归迭代器方法。它可用于查找任何数据类型的完整笛卡尔积,而不仅仅是char我很感兴趣,如果可以去除它。

<强>实施

public static class MultiCartesianExtension
{
    public static IEnumerable<TInput[]> MultiCartesian<TInput>(this IEnumerable<IEnumerable<TInput>> input)
    {
        return input.MultiCartesian(x => x);
    }

    public static IEnumerable<TOutput> MultiCartesian<TInput, TOutput>(this IEnumerable<IEnumerable<TInput>> input, Func<TInput[], TOutput> selector)
    {
        var inputList = input.ToList();
        var buffer = new TInput[inputList.Count];
        var results = MultiCartesianInner(inputList, buffer, 0);
        var transformed = results.Select(selector);
        return transformed;
    }

    private static IEnumerable<TInput[]> MultiCartesianInner<TInput>(IList<IEnumerable<TInput>> input, TInput[] buffer, int depth)
    {
        foreach (var current in input[depth])
        {
            buffer[depth] = current;
            if (depth == buffer.Length - 1)
            {
                var bufferCopy = (TInput[])buffer.Clone();
                yield return bufferCopy;
            }
            else
            {
                foreach (var a in MultiCartesianInner(input, buffer, depth + 1))
                {
                    yield return a;
                }
            }
        }
    }

<强>用法:

var input = new string[]
{
    "AB",
    "123",
    "@#",
};

foreach (var result in input.MultiCartesian(x => new string(x)))
{
    Console.WriteLine(result);
}

// Results:
// A1@
// A1#
// A2@
// A2#
// A3@
// A3#
// B1@
// B1#
// B2@
// B2#
// B3@
// B3#

对于您的特定情况,可以通过不创建bufferCopy并返回buffer本身来提高效率。我这样做是为了确保通用的使用安全性。这是因为要正常运行,buffer需要从外部不加修改。