BZOJ 1009 [HNOI2008]GT考试 - KMP+计数DP+矩阵快速幂优化

4 篇文章 0 订阅
3 篇文章 0 订阅
题意:给定长为m的串,问一个长度为n的串不出现此串的可能种数。


分析:
参考BZOJ1030,这道题是单串,将AC自动机改为KMP即可(其实如此小范围的m可以暴力)考虑到数据范围,肯定需要矩阵优化,于是思考DP方程
参考链接:http://blog.csdn.net/jeremygjy/article/details/50779475
                    http://blog.csdn.net/cjk_cjk/article/details/43038377


建立一个一维数组ans,ans[i]表示当前长度下满足状态i的长串个数即在n长度下ANS=Σ(0<=i<m)ans[i](不能与n相等)ans在0长度下初始条件为ans[0]=1
建立一个二维数组a,a[i][j]表示i状态加上一个数转化为j状态的可能种类,其中状态i意义为未知串的后缀i位和已知小串的前缀i位相等的状态(注意这里指的是严格意义上后i位,即i+1位不相等否则存在包含关系)

转移关于长度的状态,发现ans与a相乘恰好是ans的下一长度状态,于是连乘n次a,用矩阵快速幂完成操作即可。


#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int maxn=320;

int n,len,mod;
char s[maxn];
int fail[maxn];

struct matrix
{
	int m[maxn][maxn];
}a,ans;

matrix matrix_mul(matrix x,matrix y)
{
	matrix res;
	memset(res.m,0,sizeof res.m);
	for(int i=0;i<len;i++)
		for(int j=0;j<len;j++)
			for(int k=0;k<len;k++)
				res.m[i][j]=(res.m[i][j]+x.m[i][k]*y.m[k][j])%mod;
	return res;
}
void matrix_pow()
{
	int k=n;
	while(k)
	{
		if(k&1)ans=matrix_mul(ans,a);
		a=matrix_mul(a,a);
		k>>=1;
	}
	int res=0;
	for(int i=0;i<len;i++)
		res=(res+ans.m[0][i])%mod;
	printf("%d",res);
}
int main()
{
	scanf("%d%d%d",&n,&len,&mod);
	scanf("%s",s+1);
	for(int i=1;i<len;)
	{
		int j=fail[i];
		while(s[i+1]!=s[j+1]&&j)j=fail[j];
		if(s[i+1]==s[j+1])i++,j++;
		else i++;
		fail[i]=j;
	}
	for(int i=0;i<len;i++)
		for(int j='0';j<='9';j++)
	{
		int now=i;
		while(s[now+1]!=j&&now)now=fail[now];
		if(s[now+1]==j)a.m[i][now+1]=(a.m[i][now+1]+1)%mod;
		else a.m[i][0]=(a.m[i][0]+1)%mod;
	}
	ans.m[0][0]=1;
	matrix_pow();
}
//http://blog.csdn.net/jeremygjy/article/details/50779475
//http://blog.csdn.net/cjk_cjk/article/details/43038377


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值