http://icpc.upc.edu.cn/problem.php?cid=1696&pid=10
应该是有O(n)的做法的,但是目前来说我只会O(n2)O(n2)的做法,讲一下O(n2)的吧。
记f[i][j]为按了i下键盘之后产生了长度为j的串的方案数,初始f[0][0]=1。
对于某个f[i][j],要么按一次数字键(0或者1)变成f[i + 1][j + 1],要么按一次退格键变成f[i + 1][j - 1],也就是说,每一个f[i][j]只会对两个状态产生影响,但是一个f[i][j]会被很多状态影响,显然从i推i + 1会简单很多,则有:
f[i + 1][j + 1] += 2 * f[i][j]
f[i + 1][max(j - 1, 0)] += f[i][j]
注意j = 0的时候相当于屏幕上此时没有字符,此时按退格键虽然不会改变字符,但仍然是一个有效状态,所以取max(j - 1, 0)。这样仅仅是得到了按i下键盘时得到了长度为j的串的方案数,但是题目里所给的串是一个特定的串,是所有长度为j的串的其中之一,又因为长度为j的串中每个字符要么是0要么是1,总共有2 j2 j种串,所以按n下得到某个长度为len的串的方案数应为
f[n][len]/2^j。
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 5005
#define p 1000000007
#define ll long long
using namespace std;
int n,l,i,j,k,a[N][N];
char c[N];
ll ksm(ll a,ll b){
ll s=1;
while (b){
if (b&1){
s=s*a%p;
}
a=a*a%p;
b=b>>1;
}
return s;
}
int main() {
scanf("%d",&n);
scanf("%s",c);
l=strlen(c);
a[0][0]=1;
for (i=0; i<=n; i++) {
for (j=0; j<=i; j++) {
a[i+1][j+1]=(1ll*a[i][j]*2%p+a[i+1][j+1])%p;
a[i+1][max(j-1,0)]=(a[i][j]+a[i+1][max(j-1,0)])%p;
}
}
printf("%d\n",ksm(ksm(2,l),p-2)*a[n][l]%p);
}