题目
在 0 和 1 组成的长度为n(1<=n<=31)的字符串中,统计包含m(1<=m<=n)个连续 1 子串的字符串的个数。
数据
1 1
2 1
3 1
4 3
10 3
10 5
20 10
20 15
31 20
31 1
-1 -1
思路
当m>n时候 0,n全是1也无法凑出来满足的字符串
当m==n时候 1,有且只有一个满足的字符
当m<n时候 我们假设满足条件的字符串为f(m,n)
当n变成n+1的时候
①
不难发现,在f(m,n)的基础上无论加1或者加0,得到的新的字符串都满足题目要求的“存在连续m个数字都是1
”这个条件
②
但是我们发现当 长度为n的字符串的末尾m-1个数字都恰好为1(且倒是第m个数字是0的时候)的时候,我们再加上一个1,就会导致新生成的n+1长度的字符串的末尾恰好有m个字符串
满足该字符串(末尾m-1个数字是1,倒数第m个数字是0)的情况有
2
n
−
m
−
1
种
2^{n-m-1}种
2n−m−1种
注意:倘若在前n-m-1中的数字里面存在连续m个字符串,那么这种情况已经被情况①包含了,所以我们需要减去对应的已经被情况①包含的情况。
f
(
m
,
n
−
m
−
1
)
f(m,n-m-1)
f(m,n−m−1)
所以我们可以得到递推关系
f
(
m
,
n
+
1
)
=
2
f
(
m
,
n
)
+
2
n
−
m
−
1
−
f
(
m
,
n
−
m
−
1
)
f(m,n+1) = 2f(m,n) +2^{n-m-1} - f(m,n-m-1)
f(m,n+1)=2f(m,n)+2n−m−1−f(m,n−m−1)
代码(递归)
ll RE(int n, int m)
{
if(n < m) return 0;
if(n == m) return 1;
return 2 * RE(n - 1,m) + pow(2, n-m-1) - RE(n - m - 1,m);
}
代码(迭代)
#include<iostream>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
ll IT(int n , int m)
{
ll *arr = new ll[n + 1];
memset(arr,0,8*n+8);
for(int i = 1; i < n + 1; i++)
{
if(i < m)
{arr[i] = 0;}
else if(i == m)
{arr[i] = 1;}
else if(i > m)
{
arr[i] = arr[i - 1] * 2 + pow(2,i - m - 1) - arr[i - m - 1];
}
}
ll temp = arr[n];
delete[] arr;
return temp;
}
int main()
{
ll n, m;
while(cin >> n >> m)
{
if(n == -1 && m == -1)
break;
cout << IT(n,m) << endl;
//cout << RE(n,m) << endl;
}
}