给定一个字符串,计算字符串的排列数,不重复(和禁止的字符)

时间:2016-04-30 19:42:54

标签: string algorithm math combinatorics

我现在已经在几个小时内遇到了算法问题。

问题的(幻想)陈述如下:

我们的花园包含一排花。您将获得String花园中该行的当前内容。花园中的每个角色代表一朵花。不同的字符代表不同的颜色。相同颜色的花看起来都一样。您可以将花园中的花朵重新排列成您喜欢的任何顺序。 (在形式上,你可以在你的花园中交换任意两朵花,你可以任意多次这样做。)你也被给予一个与花园相同长度的字符串禁止。你想把花园重新安排成一个新的字符串G,它将满足以下条件:

没有两个相邻的花具有相同的颜色。通常,对于每个有效的i,G [i]和G [i + 1]必须不同。 对于每个有效的i,G [i]不得等于禁止[i]。 令X为满足上述所有条件的不同字符串G的数量。计算并返回数字(X模数1,000,000,007)。

只是用一个例子来澄清:X(“aaabbb”,“cccccc”)= 2(“ababab”和“bababa”)

我一直在尝试计算字符串中有多少个字母(示例中为'a' - > 3,'b' - > 4),然后递归计算不同的可能性(跳过是否有重复或禁止的信件)。这些方面的东西:

using Map = std::map < char, size_t > ;
Map hist; 
std::string forbid;

size_t countRecursive(std::string s, size_t len)
{
    if (len == 0)
        return 1;

    size_t curPos = s.size() ;
    size_t count(0);

    for (auto &p : hist) {
        auto key = p.first;

        if (hist[key] == 0) continue;
        if (forbid[curPos] == key) continue;
        if (curPos > 0 && s[curPos - 1] == key) continue;

        hist[key]--;            
        count += countRecursive(s + key, len - 1);
        hist[key]++;
    }


    return count;
}

之前初始化hist和forbid的位置。但是,这似乎是n!并且因为n可以<= 15,所以它确实在复杂性方面爆炸。

我并不是在寻找一个完整的解决方案。只是,如果你对我应该解决问题的方式有任何建议,我将非常感激!

1 个答案:

答案 0 :(得分:0)

我接近它如下:你的'禁止'字符串和'花园'一样长。这意味着给定N个字符的字母表,每个位置G [i]最多可以有N-1个可能的字符(因为一个将被禁止)。这给你一个仅受N限制的上界。如果该界限小于模数,它可能会导致一些有趣的考虑因素,但让我们继续前进。

现在一个非常基本的方法是计算组合:如果花园长K字符,第一项G [0]将具有N-1种可能性;如果禁止[1]与G [0]不同,则第二个G [1]将具有N-2个可能性,如果禁止则为N-1 [1] == G [0]。第三个字符G [2]也有N-2种可能性,取决于禁用[2]和G [1],依此类推。

为了清楚起见:N-2来自N-1可能性的事实,必须除去另一个,即字符串中前面的字符的值,除非这个字符与当前位置的禁止字符匹配

因此,如果禁止[i + 1]总是与G [i]不同,则你有N-1 * N-2 * N-2 * ... * N-2,K次。这是你的下限。

现在在上限和下限之间有许多字符串,例如禁止[i + 1]仅对第二个位置等于G [i];第二个和第三个;所以你的字符串数量是:

N-1 * N-2 * N-2 * N-2 ... K
N-1 * N-1 * N-2 * N-2 ... K
N-1 * N-1 * N-1 * N-2 ... K

等等,直到你有一个字符串,每个字符可以有N-1种可能性。

换句话说,

N-1 * (N-2)^K-1
(N-1)^2 * (N-2)^K-2
(N-1)^3 * (N-2)^K-3

你能拥有多少个字符串?这取决于K的大小,即你的花园有多大:)

也就是说,假设我正确理解了问题。