题意
定义F(x)为F(x-1)与F(x-2)的连接(其中F(0) = ‘0’,F(1) = ‘1’)。
给出一个长度不超过100的字符串s,询问s在F(x)的所有子序列中出现了多少次。
题解
数量很大的计数问题,我们首先想到的解决方案就是dp。
我们考虑F(x) = F(x-1) + F(x-2)
是由两部分构成的,我们可以分治来计算。
s[1,n]在F(x)中出现的次数由几部分构成:
- s[1,n]s[1,n]在F(x−1)F(x−1)中出现的次数乘以2len(F(x−2))2len(F(x−2))
- s[1,n]s[1,n]在F(x−2)F(x−2)中出现的次数乘以2len(F(x−1))2len(F(x−1))
- s[1,k]s[1,k]在F(x−1)F(x−1)中出现的次数*s[k+1,n]s[k+1,n]在F(x−2)F(x−2)中出现的次数
我们定义状态dp[i][l][r]dp[i][l][r]表示s[l,r]s[l,r]在F(i)F(i)的所有子序列中出现的次数。
那么
dp[i][l][r]+=dp[i−1][l][r]∗2len(F(i−2));当r==n时候dp[i][l][r]+=dp[i−1][l][r]∗2len(F(i−2));当r==n时候
解释:当r==n的时候,由于s[l,r]已经在F(i-1)中结尾了,所以F(i-2)中可以随便选取组成新的字串。
dp[i][l][r]+=dp[i−1][l][r]∗1;当r!=n时候dp[i][l][r]+=dp[i−1][l][r]∗1;当r!=n时候
解释:当r!=n的时候,由于s[l,r]没有在F(i-1)中结尾,此时如果取F(i-2)中字符的话,会给后面造成影响,即拼接出的包含s不是连续的。
dp[i][l][r]+=dp[i−2][l][r]∗2len(F(i−1));当l==1时候dp[i][l][r]+=dp[i−2][l][r]∗2len(F(i−1));当l==1时候
dp[i][l][r]+=dp[i−2][l][r]∗1;当l!=1时候dp[i][l][r]+=dp[i−2][l][r]∗1;当l!=1时候
然后就是s[l,r]s[l,r]分两段s[l,k]s[l,k]在F(i−1)F(i−1)里面和s[k+1,r]s[k+1,r]在F(i−2)F(i−2)里面。
dp[i][l][r]+=dp[i][l][k]∗dp[i][k+1][r],l<=k<rdp[i][l][r]+=dp[i][l][k]∗dp[i][k+1][r],l<=k<r
实现代码
#include <iostream>
#include <cstdio>
using namespace std;
#define pr(x) cout<<#x<<':'<<x<<endl
typedef long long ll;
const int maxn = 107;
const ll mod = 1e9+7;
ll dp[maxn][maxn][maxn];
char s[maxn];
int n,m;
ll modpow2[maxn];
int main(){scanf("%d%d",&m,&n);scanf(" %s",s);modpow2[0] = modpow2[1] = 2;for(int i = 2;i <= n;++i)modpow2[i] = modpow2[i-1]*modpow2[i-2]%mod;for(int i = 0;i < m;++i){dp[s[i]-'0'][i+1][i+1] = 1;}for(int i = 2;i <= n;++i){for(int l = 1;l <= m;++l){for(int r = l;r <= m;++r){dp[i][l][r] += dp[i-1][l][r]*(r == m?modpow2[i-2]:1)%mod;dp[i][l][r] += dp[i-2][l][r]*(l == 1?modpow2[i-1]:1)%mod;for(int k = l;k < r;++k){dp[i][l][r] += dp[i-1][l][k]*dp[i-2][k+1][r]%mod;}dp[i][l][r] %= mod;}}}cout<<dp[n][1][m]<<endl;return 0;
}