- Description
求有多少个n位的数字串不包含m位的字符串(范围 n <= 1e9 n<=1e9, m <= 20m<=20)
- Solution
f[i][j]表示以数字串i位结尾有j个匹配的字符
g[i][j]表示从i个字符匹配到j个字符匹配的方案数
dp方程如下:
f[i][j]=f[i-1][k]*g[k][j] (0<=k<m)
发现g数组可以用kmp预处理出来,然后上矩阵乘法
- Code
#include <cstdio> #include <cstdlib> #include <cstring> #define ll long long using namespace std; const int N=21; ll n,p,g[N][N],d[N][N],pre[N][N]; int m,nxt[N]; char s[N]; void mul1() { memset(pre,0,sizeof(pre)); for(int i=0;i<m;i++) for(int j=0;j<m;j++) for(int k=0;k<m;k++) pre[i][j]=(pre[i][j]+g[i][k]*g[k][j])%p; for(int i=0;i<m;i++) for(int j=0;j<m;j++) g[i][j]=pre[i][j]%p; } void mul2() { memset(pre,0,sizeof(pre)); for(int i=0;i<m;i++) for(int j=0;j<m;j++) for(int k=0;k<m;k++) pre[i][j]=(pre[i][j]+d[i][k]*g[k][j])%p; for(int i=0;i<m;i++) for(int j=0;j<m;j++) d[i][j]=pre[i][j]%p; } void ksm(ll x) { while(x) { if(x&1) mul2(); mul1(),x>>=1; } } int main() { scanf("%lld%d%lld",&n,&m,&p); scanf("%s",s+1); nxt[1]=0; int k=0; for(int i=2;s[i];i++) { while(k>0 && s[k+1]!=s[i]) k=nxt[k]; if(s[k+1]==s[i]) k++; nxt[i]=k; } for(int i=0;i<m;i++) for(int j=0;j<=9;j++) { char wh='0'+j; int k=i; while(k && s[k+1]!=wh) k=nxt[k]; if(s[k+1]==wh) k++; if(k<m) g[i][k]++; } for(int i=0;i<m;i++) d[i][i]=1; ksm(n); ll ans=0; for(int i=0;i<m;i++) ans=(ans+d[0][i])%p; printf("%lld\n",ans%p); return 0; }