P3193 [HNOI2008]GT考试

题意

给定数字串 A[1..m]
构造一个数字串 X[1..n],使得串中不出现数字串 A
求出方案数模 k 的结果 n≤10^9 ,m≤20,k≤1000

分析

设答案为f[][].
f[i][j]表示当前X是在1-i时,匹配到A的第j个数字.
如果i+1选择的数字=A[j+1],那么就直接转移.
否则转移到f[i+1][next[j]+1].
对于A的每一个位置,每一次转移的方案数都是一定的.
所以可以直接跑一个矩阵,在乘上n次幂就可以了.
矩阵a[i][j]表示从第j位开始构造,最后匹配到第i位的方案数.此处i是下一个应该匹配的.
eg:如果i==m,那么就相当于我刚刚匹配的是第m-1位,匹配成功了,但是还没有匹配m,也是一个方案.

代码

#include<bits/stdc++.h>
using namespace std;
const int N=104;
int n,m,p,nex[N];
char s[N];
struct Kano{
	int a[20][20];
	Kano(){
		memset(a,0,sizeof a);
	}
	void get(){
		for(int i=0;i<m;i++)
			a[i][i]=1;
	}
	Kano operator*(const Kano &b)const{
		Kano c;
		for(int k=0;k<m;k++)
			for(int i=0;i<m;i++)
				for(int j=0;j<m;j++)
					c.a[i][j]+=a[i][k]*b.a[k][j],c.a[i][j]%=p;
		return c;
	}
}kano,f;
void kmp(){
	int j=0;
	for(int i=1;i<m;i++){
		while(j&&s[i]!=s[j]) j=nex[j];
		if(s[i]==s[j]) j++;
		nex[i+1]=j;
	}
}
Kano ksm(int x){
	if(x==1)
		return kano;
	Kano t=ksm(x>>1);
	t=t*t;
	if(x&1)
		t=t*kano;
	return t;
}
int main(){
	std::ios::sync_with_stdio(false);
	cin>>n>>m>>p>>s;
	kmp();
	for(int i=0;i<m;i++)
		for(int ch='0';ch<='9';ch++){
			int j=i;
			while(j&&s[j]!=ch) j=nex[j];
			if(s[j]==ch) j++;
			if(j!=m)
				kano.a[j][i]++;
		}
	/*Kano k=kano*kano;
	for(int i=0;i<m;++i){
		for(int j=0;j<m;++j)
			printf("%d",k.a[i][j]);
		printf("\n");
	}*/
	f.a[0][0]=1;
	f=ksm(n)*f;
	int ans=0;
	for(int i=0;i<m;i++)
		ans=(ans+f.a[i][0])%p;
	cout<<ans;
}
posted @ 2019-12-16 09:47  贰冬  阅读(160)  评论(1编辑  收藏  举报