寻找禁止字符串计划

时间:2013-11-04 17:47:05

标签: c++ string algorithm dynamic-programming combinatorics

我最近遇到了以下问题:

如果有三个连续的字母,其中一个是A,一个是B,则称一个字符串被禁止 一个是C.例如BAAAACABCC是禁止的,但AAABBBCCC不是。 你得到一个整数n。你必须找到多少长度为n的字符串是不被禁止的。 (n将从1到30)
示例:如果n = 2,则禁止任何字符串。所以输出是9。

我试过但找不到有效的解决方案。我为此写了一个暴力算法,其中我检查所有可能的这样的字符串,但由于它是一个指数算法,它是该死的慢。您可以找到我的代码here

有人可以为我指导一个有效的算法,可能使用动态编程或任何其他方式。

谢谢

4 个答案:

答案 0 :(得分:2)

从系列的外观来看:3,9,21,51,123下一个数字是123 * 2 + 51 = 297

答案 1 :(得分:1)

动态编程可以在这里应用。

假设你知道n = k的非禁止序列的数量。现在,如果您添加另一个字母,那么组合的数量将变为3k。但是你必须放弃被禁止的新组合。

任何序列的最后两个字母可以是:

ab
ac
bc
ba
ca
cb
bb
aa
cc

似乎形成了你提供的系列 要丢弃的组合数(k + 1)=(k)

的新增加的数量

编辑: 为什么会这样? 从3k组合中说,我们首先丢弃k组合,说出我们将丢弃1/3的所有先前序列。

现在废弃了这些k组合,有一些组合在末尾有一个重复的字母(aa,bb或cc)。我们不必丢弃那些序列。

这些序列的数量应该等于(k-1)的非禁止序列的数量,因为对于我们为n = k做的每个新序列,我们可以有一个且只有一个重复的新序列信。

Hence if f(k) = number of unforbidden combinations for n = k, 
 then f(k + 1) = 3f(k) - [f(k)-f(k-1)]. 

For example for n=3 the number of sequences with repeated letter at the end shall be 9.  
For n=4 the number of sequences with repeated letter at the end shall be 21.  
... and so on.

答案 2 :(得分:1)

这种动态编程方法如何,其中C[x,y]表示长度为x的非禁止字符串的数量,以两个字符序列y结尾

C [n, 'AA'] = C[n-1, 'AA'] + C[n-1, 'AB'] + C[n-1, 'AC']
C [n, 'AB'] = C[n-1, 'BA'] + C[n-1, 'BB']
C [n, 'AC'] = C[n-1, 'CA'] + C[n-1, 'CB'] + C[n-1, 'CC']
C [n, 'BA'] = C[n-1, 'AA'] + C[n-1, 'AB'] + C[n-1, 'AC']
C [n, 'BB'] = C[n-1, 'BA'] + C[n-1, 'BB'] + C[n-1, 'BC']
C [n, 'BC'] = C[n-1, 'CA'] + C[n-1, 'CB'] + C[n-1, 'CC']
C [n, 'CA'] = C[n-1, 'AA'] + C[n-1, 'AB'] + C[n-1, 'AC']
C [n, 'CB'] = C[n-1, 'BA'] + C[n-1, 'BB'] + C[n-1, 'BC']
C [n, 'CC'] = C[n-1, 'CA'] + C[n-1, 'CB'] + C[n-1, 'CC']

边界条件n >= 3, C[3, x] = 3, C[3, 'AB'] = 2

一个简单的程序,不确定是否正确,但肯定会冒一些低估的风险,如果它是错的,呵呵:)

#include <iostream>

unsigned int C[100][9];

// AA  0
// AB  1
// AC  2
// BA  3
// BB  4
// BC  5
// CA  6
// CB  7
// CC  8

void
calc (unsigned int n) {
    unsigned int i;
    for (i = 0; i < 9; ++i)
        C[3][i] = 3;
    C[3][1] = 2;

    for (i = 4; i <= n; ++i) {
        C[i][0] = C[i-1][0] + C[i-1][1] + C[i-1][2];
        C[i][1] = C[i-1][3] + C[i-1][4];
        C[i][2] = C[i-1][6] + C[i-1][7] + C[i-1][8];
        C[i][3] = C[i-1][0] + C[i-1][1] + C[i-1][2];
        C[i][4] = C[i-1][3] + C[i-1][4] + C[i-1][5];
        C[i][5] = C[i-1][6] + C[i-1][7] + C[i-1][8];
        C[i][6] = C[i-1][0] + C[i-1][1] + C[i-1][2];
        C[i][7] = C[i-1][3] + C[i-1][4] + C[i-1][5];
        C[i][8] = C[i-1][6] + C[i-1][7] + C[i-1][8];
    }
}

// C [n, 'AA'] = C[n-1, 'AA'] + C[n-1, 'AB'] + C[n-1,'AC']
// C [n, 'AB'] = C[n-1, 'BA'] + C[n-1, 'BB']
// C [n, 'AC'] = C[n-1, 'CA'] + C[n-1, 'CB'] + C[n-1, 'CC']
// C [n, 'BA'] = C[n-1, 'AA'] + C[n-1, 'AB'] + C[n-1, 'AC']
// C [n, 'BB'] = C[n-1, 'BA'] + C[n-1, 'BB'] + C[n-1, 'BC']
// C [n, 'BC'] = C[n-1, 'CA'] + C[n-1, 'CB'] + C[n-1, 'CC']
// C [n, 'CA'] = C[n-1, 'AA'] + C[n-1, 'AB'] + C[n-1, 'AC']
// C [n, 'CB'] = C[n-1, 'BA'] + C[n-1, 'BB'] + C[n-1, 'BC']
// C [n, 'CC'] = C[n-1, 'CA'] + C[n-1, 'CB'] + C[n-1, 'CC']



int
main () {
    for (unsigned int i = 3; i < 10; ++i) {
        calc (i);

        unsigned int s = 0;
        for (unsigned int j = 0; j < 9; ++j)
            s += C[i][j];
        std::cout << s << " ";
    }
}

答案 3 :(得分:0)

i >= 2

same[i]为允许(即非禁止)长度为i的字符串数,以两个相同的字符结尾。
diff[i]为允许的长度为i的字符串数,以两个不同的字符结尾。

然后same[2] = 3diff[2] = 6i >= 2

same[i+1] = same[i] + diff[i]  
diff[i+1] = 2 * same[i] + diff[i]

我们想要的是n[i] = s[i] + d[i];上面的等式给我们

n[2] = 9
n[i+1] = 2 * n[i] + n[i-1]

使用此功能,您可以在基本线性时间内计算n[i]