【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)

【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)

题面

BZOJ

题解

看到这个题目
化简一下题意
长度为\(n\)的,由\(0~9\)组成的字符串中
不含串\(s\)的串的数量有几个

很显然,如果组成的字符串和\(s\)串做\(KMP\)的匹配的话
是不能匹配到最后一位的

所以,我们想到一个很显然的方程
\(f[i][j]\)表示当前做了第\(i\)位,在\(s\)串中匹配到了第\(j\)
每次枚举下一位放的数字
以及每一位的位置
相当于做\(KMP\)的匹配
然后进行转移

所以,我们可以写出一个暴力

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
inline int read()
{
	int x=0,t=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=-1,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return x*t;
}
int f[2000][30];
int nt[30],n,m,K;
char s[30];
void Get_Next(char *s)
{
	int l=strlen(s+1);
	nt[1]=0;
	for(int i=2;i<=l;++i)
	{
		int t=nt[i-1];
		while(t&&s[i]!=s[t+1])t=nt[t];
		if(s[i]==s[t+1])t+=1;
		nt[i]=t;
	}
}
int main()
{
	n=read();m=read();K=read();
	scanf("%s",s+1);
	Get_Next(s);
	f[0][0]=1;
	for(int i=0;i<n;++i)
	{
		for(int j='0';j<='9';++j)
		{
			for(int k=0;k<m;++k)
			{
				int t=k;
				while(t&&s[t+1]!=j)t=nt[t];
				if(j==s[t+1])t++;
				(f[i+1][t]+=f[i][k])%=K;
			}
		}
	}
	int ans=0;
	for(int i=0;i<m;++i)ans+=f[n][i];
	printf("%d\n",ans%K);
	return 0;
}

\(n\)的范围有\(10^9\)
不可能是\(O(n)\)解了
我们发现每次匹配的转移关系是一定的
所以可以用矩阵快速幂来优化\(dp\)转移

复杂度为\(O(n+m^3logn)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
inline int read()
{
	int x=0,t=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=-1,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return x*t;
}
int nt[30],n,m,K;
char s[30];
void Get_Next(char *s)
{
	int l=strlen(s+1);
	nt[1]=0;
	for(int i=2;i<=l;++i)
	{
		int t=nt[i-1];
		while(t&&s[i]!=s[t+1])t=nt[t];
		if(s[i]==s[t+1])t+=1;
		nt[i]=t;
	}
}
struct Dalao
{
	int s[30][30];
	void init()
		{
			memset(s,0,sizeof(s));
			for(int i=0;i<m;++i)s[i][i]=1;
		}
	void clear(){memset(s,0,sizeof(s));}
}G;
Dalao operator*(Dalao a,Dalao b)
{
	Dalao ret;ret.clear();
	for(int i=0;i<m;++i)
		for(int j=0;j<m;++j)
			for(int k=0;k<m;++k)
				(ret.s[i][j]+=a.s[i][k]*b.s[k][j]%K)%=K;
	return ret;
}
Dalao fpow(Dalao a,int b)
{
	Dalao s;s.init();
	while(b){if(b&1)s=s*a;a=a*a;b>>=1;}
	return s;
}
int main()
{
	n=read();m=read();K=read();
	scanf("%s",s+1);
	Get_Next(s);
	for(int j='0';j<='9';++j)
	{
		for(int k=0;k<m;++k)
		{
			int t=k;
			while(t&&s[t+1]!=j)t=nt[t];
			if(j==s[t+1])t++;
			G.s[k][t]++;
		}
	}
	G=fpow(G,n);
	int ans=0;
	for(int i=0;i<m;++i)ans=(ans+G.s[0][i])%K;
	printf("%d\n",ans%K);
	return 0;
}

posted @ 2018-01-18 15:42  小蒟蒻yyb  阅读(344)  评论(5编辑  收藏  举报