【BZOJ4861】【BJOI2017】—魔法咒语(AC自动机+矩阵快速幂优化dp)

传送门

l ≤ 100 l\le 100 l100
显然的自动机上 d p dp dp就完了

l ≤ 1 e 8 l\le1e8 l1e8时直接 d p dp dp显然是不行的
但是发现基本串长度不大于二
考虑矩乘优化
a [ i ] [ j ] a[i][j] a[i][j]表示有几个基本串使得在自动机上 i i i走到 j j j
如果所有串长相同就可以直接快速幂了
但是串长有 1 1 1 2 2 2
不同长度不好处理拼起来的情况
A 1 A_1 A1表示长度为1的串的转移矩阵
A 2 A_2 A2为长度为2的
考虑构造矩阵

[ 0 A 2 I A 1 ] \begin{bmatrix} 0&A_2\\ I &A_1 \end{bmatrix} [0IA2A1]

这样的转移矩阵就可以了

#include<bits/stdc++.h>
using namespace std;
#define gc getchar
inline int read(){
	char ch=gc();
	int res=0,f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
int n,m,l;
const int N=55,M=105;
const int mod=1e9+7;
inline int add(int a,int b){return (a+=b)>=mod?a-=mod:a;}
inline void Add(int &a,int b){(a+=b)>=mod?a-=mod:0;}
inline int dec(int a,int b){return (a-=b)<0?a+=mod:a;}
inline void Dec(int &a,int b){(a-=b)<0?a+=mod:0;}
inline int mul(int a,int b){return 1ll*a*b>=mod?1ll*a*b%mod:a*b;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int ksm(int a,int b,int res=1){
    for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));return res;
}
inline void chemx(int &a,int b){a<b?a=b:0;}
inline void chemn(int &a,int b){a>b?a=b:0;}
namespace Ac{
	int nxt[M][26],tot,fail[M],ed[M];
	inline void insert(char *s){
		int p=0;
		for(int i=1,len=strlen(s+1);i<=len;i++){
			int c=s[i]-'a';
			if(!nxt[p][c])nxt[p][c]=++tot;
			p=nxt[p][c];
		}
		ed[p]++;
	}
	queue<int> q;
	inline void build(){
		for(int i=0;i<26;i++){
			if(nxt[0][i])fail[nxt[0][i]]=0,q.push(nxt[0][i]);
		}
		
		while(!q.empty()){
			int p=q.front();q.pop();
			for(int c=0;c<26;c++){
				int v=nxt[p][c];
				if(!v)nxt[p][c]=nxt[fail[p]][c];
				else fail[v]=nxt[fail[p]][c],q.push(v);
			}
			ed[p]+=ed[fail[p]];
		}
	}
	inline int go(int p,char *s){
		if(ed[p])return -1;
		for(int i=1,len=strlen(s+1);i<=len;i++){
			int c=s[i]-'a';
			p=nxt[p][c];
			if(ed[p])return -1;
		}
		return p;
	}
}
using namespace Ac;
char s[N][M],t[N][M];
int len[N];
namespace solve1{
	int f[M][M];
	inline void main(){
		f[0][0]=1;
		for(int i=0;i<l;i++)
		for(int j=0;j<=tot;j++)
			for(int k=1;k<=n;k++)
			if(i+len[k]<=l){
				int p=go(j,s[k]);
				if(p!=-1)
					Add(f[i+len[k]][p],f[i][j]);
			}
		int res=0;
		for(int i=0;i<=tot;i++)Add(res,f[l][i]);
		cout<<res<<'\n';
	}
}
namespace solve2{
	int lim;
	struct mat{
		int a[M*2][M*2];
		mat(){memset(a,0,sizeof(a));}
		friend inline mat operator *(const mat &a,const mat &b){
			mat c=mat();
			for(int i=0;i<=lim;i++)
			for(int k=0;k<=lim;k++)
			for(int j=0;j<=lim;j++)
			Add(c.a[i][j],mul(a.a[i][k],b.a[k][j]));
			return c;
		}
	}ret,ans;
	inline mat ksm(mat a,int b,mat res){
		for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
		return res;
	}
	inline void main(){
		lim=tot*2+1;
		for(int i=0;i<=tot;i++)ret.a[i+tot+1][i]=1;
		for(int i=0;i<=tot;i++)
		for(int j=1;j<=n;j++){
			if(len[j]==2){
				int p=go(i,s[j]);
				if(p!=-1)ret.a[i][p+tot+1]++;
			}
		}
		for(int i=0;i<=tot;i++)
		for(int j=1;j<=n;j++){
			if(len[j]==1){
				int p=go(i,s[j]);
				if(p!=-1)ret.a[i+tot+1][p+tot+1]++;
			}
		}
		ret=ksm(ret,l-1,ret);
		ans.a[0][tot+1]=1;
		ans=ans*ret;
		int res=0;
		for(int i=0;i<=tot;i++)
		Add(res,ans.a[0][i+tot+1]);
		cout<<res;
	}
}
int main(){
	n=read(),m=read(),l=read();
	for(int i=1;i<=n;i++)scanf("%s",s[i]+1),len[i]=strlen(s[i]+1);;
	for(int i=1;i<=m;i++)scanf("%s",t[i]+1),insert(t[i]);
	build();
	if(l<=100)solve1::main();
	else solve2::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值