我最近遇到了以下问题:
如果有三个连续的字母,其中一个是A,一个是B,则称一个字符串被禁止
一个是C.例如BAAAACABCC是禁止的,但AAABBBCCC不是。
你得到一个整数n。你必须找到多少长度为n的字符串是不被禁止的。 (n将从1到30)
示例:如果n = 2,则禁止任何字符串。所以输出是9。
我试过但找不到有效的解决方案。我为此写了一个暴力算法,其中我检查所有可能的这样的字符串,但由于它是一个指数算法,它是该死的慢。您可以找到我的代码here
有人可以为我指导一个有效的算法,可能使用动态编程或任何其他方式。
谢谢
答案 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] = 3
,diff[2] = 6
和i >= 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]
。