#KMP,矩阵乘法#洛谷 3193 [HNOI2008]GT考试
题目
给定\(n,m,K\)和一个长度为\(m\)的数\(x\),
问有多少个\(n\)位数满足任意一段不与\(x\)完全相同,可含前导0
\(n\leq 10^9,m\leq 20\)
分析
设\(dp[i][j]\)表示前\(i\)个数位匹配到\(x\)的第\(j\)位的方案数,
可以发现加入一个新的字母不一定重新开始匹配,所以需要求出最长公共前后缀,
用KMP实现,至于\(n\leq 10^9\)可以用矩阵乘法维护转移即可
代码
#include <cstdio>
#include <cstring>
#define rr register
using namespace std;
struct maix{int p[20][20];}A,ANS;
int n,m,mod,fail[21],ans; char s[21];
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline maix mul(maix A,maix B){
rr maix C;
memset(C.p,0,sizeof(C.p));
for (rr int i=0;i<m;++i)
for (rr int j=0;j<m;++j)
for (rr int k=0;k<m;++k)
C.p[i][j]=mo(C.p[i][j],A.p[i][k]*B.p[k][j]%mod);
return C;
}
signed main(){
scanf("%d%d%d%s",&n,&m,&mod,s+1);
for (rr int i=2,j=0;i<=m;++i){
while (j&&s[j+1]!=s[i]) j=fail[j];
fail[i]=(j+=(s[j+1]==s[i]));
}
for (rr int i=0;i<m;++i)
for (rr int P=48;P<=57;++P){
rr int j=i;
while (j&&s[j+1]!=P) j=fail[j];
j+=(s[j+1]==P);
if (j!=m) ++A.p[i][j];
}
for (rr int i=0;i<m;++i) ANS.p[i][i]=1;
for (;n;n>>=1,A=mul(A,A))
if (n&1) ANS=mul(ANS,A);
for (rr int i=0;i<m;++i) ans=mo(ans,ANS.p[0][i]);
return !printf("%d",ans);
}