Eoj 2854 统计包含m个连续1子串的字符串的个数

统计特定字串模式的个数
Time Limit:1000MS Memory Limit:65536KB
Total Submit:321 Accepted:173

Description

在0和1组成的长度为n(1≤n≤31)的字符串中,统计包含m(1≤m≤n)个连续1子串的字符串的个数。

Input

本题有多组测试数据。每组测试数据占一行,含n和m,表示字符串的长度和连续1的个数。n=-1和m=-1表示输入结束。

Output

对每组测试数据,在一行中输出统计出的字符串的个数。

Sample Input

1 1

2 1

3 1

4 3

10 3

10 5

20 10

20 15

31 20

31 1

-1 -1

Sample Output

1

3
7
3
520
112
6144
112
13312
2147483647

Notes

长度为4,包含3个连续1子串的字符串有3个:

0111,1110,1111

解题思路:逆向思考,考虑长度为n的01串中,不含m个连续1的情况数。设dp[i]为截至第i位时,不含m个连续1的情况数。那么:
1.若i < m,第i位不管是0还是1都不可能有m个连续1的情况出现,因此此时dp[i] = dp[i-1] * 2
2.若i == m,第i位若为1,会和向前回溯m-1位都是1的情况,合并成一个长度为m的连续1串;第i位若为0,则不会构成这种连续的情况。因此此时dp[i] = dp[i-1] * 2 – 1
3.若i > m,此时若i为1,且向前回溯m-1位都是1,会合并构成一个长度为m的连续1串(类似情况2),此时需要做的是在总情况数里减去这种连续的情况,那么怎么知道这种连续的情况是多少呢?其实答案就是dp[i-m-1],意味着由前面的从第一位开始到第i-m-1位的合法情况加上连续的m位为1的情况,构成了这样的一个非法串。
最后不要忘记了我们是逆向求解的,因此2^n – dp[n]才为所求。

类似的题还有VIJOS1232:https://www.vijos.org/p/1232

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int dp[35];
int main()
{
    int n,m;
    while (~scanf("%d %d", &n, &m))
    {
        if (n == -1 && m == -1) break;
        dp[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            if (i < m) dp[i] = 2 * dp[i-1];
            else if (i == m) dp[i] = 2 * dp[i-1] - 1;
            else dp[i] = 2 * dp[i-1] - dp[i-m-1];
        }
        printf("%d\n",(int)pow(2,n) - dp[n]);
    }
    return 0;
}
点赞