【BZOJ】4861: [Beijing2017]魔法咒语-AC自动机+DP+矩乘

传送门:bzoj4861


题解

首先把忌讳词建成AC自动机,所有串的 e n d end end结点和可以通过跳 f a i l fail fail链到一个 e n d end end结点的点都不能走到( g e t f a i l getfail getfail时预处理即可)。

L ≤ 100 L\leq 100 L100时,考虑在AC自动机上DP:
DP算是比较套路的了。
f [ i ] [ j ] f[i][j] f[i][j]表示已经选择了 i i i个字符,走到了自动机上 j j j结点的方案数,有
f [ j + s z k ] [ t r a n s [ j ] [ k ] ] + = f [ i ] [ j ] f[j+sz_k][trans[j][k]]+=f[i][j] f[j+szk][trans[j][k]]+=f[i][j]
其中 k k k枚举的是基本词汇, s z k sz_k szk是串长, t r a n s [ j ] [ k ] trans[j][k] trans[j][k]表示在 j j j结点时再接上串 k k k后会走到的结点。
设初始值 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1 O ( n 3 ) D P O(n^3)DP O(n3)DP即可。

L > 100 L>100 L>100时, D P DP DP的复杂度显然不可接受,但考虑到 l e n ≤ 2 len\leq 2 len2
也就是一个最多关乎 i − 1 , i − 2 i-1,i-2 i1,i2两项的线性递推式,直接矩阵快速幂即可。
复杂度 O ( n 3 l o g L ) O(n^3logL) O(n3logL)


代码

#include
using namespace std;
const int mod=1e9+7,N=110;

int n,m,L,ans,cnt,trans[N][N];
int sz[N],len,mx,ed[N];
char s[N][N],t[N];

inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}

struct AC{
    queueQ;
	int ch[N][26],f[N];
	
	inline void ins(char *s)
	{
		int i,alp,u=0;len=strlen(s);
		for(i=0;i>=1,Mul(g,g))
		 if(L&1) Mul(e,g);
		for(i=0;i<=cnt;++i){
			ad(ans,e.v[0][i]);
		}
		printf("%d",ans);
	}
}

int main(){
	int i;scanf("%d%d%d",&n,&m,&L);
	for(i=1;i<=n;++i) scanf("%s",s[i]);
	for(i=1;i<=m;++i){scanf("%s",t);ac.ins(t);}
	ac.getfail();memset(trans,0xff,sizeof(trans));
	for(i=1;i<=n;++i) ac.getrans(i,s[i]);
	if(L<=100) pA::solve();
	else pB::solve();
	return 0;
}

你可能感兴趣的:(AC自动机,矩阵加速DP)