#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);
}
posted @ 2021-08-24 09:29  lemondinosaur  阅读(32)  评论(0编辑  收藏  举报