#KMP,矩阵乘法#洛谷 3193 [HNOI2008]GT考试

题目

给定(n,m,K)和一个长度为(m)的数(x)
问有多少个(n)位数满足任意一段不与(x)完全相同,可含前导0
(nleq 10^9,mleq 20)


分析

(dp[i][j])表示前(i)个数位匹配到(x)的第(j)位的方案数,
可以发现加入一个新的字母不一定重新开始匹配,所以需要求出最长公共前后缀,
用KMP实现,至于(nleq 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);
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/15178805.html