bzoj4861 / P3715 [BJOI2017]魔法咒语
AC自动机+dp+矩阵乘法
常规思路是按基本串建立AC自动机
然鹅这题是按禁忌串建立AC自动机
对后缀是禁忌的点以及它的失配点做上标记$(a[i].ed)$,到时候不访问。
基本串转化为自动机上的边:设$p[j][i]$表示第$j$个节点加上第$i$个串会到的节点编号
在建好AC自动机后可以直接处理。
现在分类讨论(对,两份代码)
1.$L<=100,60pts$
直接在AC自动机上跑dp
设$f[j][i]$表示到点$j$长度为$i$的方案数
枚举基本串$1~k$,显然$f[p[j][k]][i+size[k]]+=f[j][i]$
$sz$表示AC自动机的节点数,则$ans=\sum_{i=0}^{sz}f[i][L]*[a[i].ed==0]$
2.基本词汇长度不超过$2,40pts$
开个$maxn*2$的矩阵,矩阵乘法瞎搞。
$f[i][L]=\sum_{j}f[j][l-1],j->i$
对于长度为2的情况,就开2倍的数组,用$i*2+1$暂时保存。
注意下标从0开始
(为啥我的常数这么大呢......)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<queue> 5 using namespace std; 6 #define N 105 7 const int mod=1e9+7; 8 int ans,sz,n,m,L,siz[N],f[N][N],p[N][N]; 9 char q[N],s[N][N]; 10 queue <int> h; 11 12 struct node{int nxt[26],f,ed;}a[5002]; 13 void add(){ 14 scanf("%s",q); 15 int len=strlen(q),u=0; 16 for(int i=0;i<len;++i){ 17 if(!a[u].nxt[q[i]-'a']) 18 a[u].nxt[q[i]-'a']=++sz; 19 u=a[u].nxt[q[i]-'a']; 20 }a[u].ed=1; 21 } 22 void acbuild(){ 23 for(int i=0;i<26;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]); 24 while(!h.empty()){ 25 int x=h.front();h.pop(); 26 for(int i=0;i<26;++i){ 27 int &to=a[x].nxt[i]; 28 if(to){ 29 a[to].f=a[a[x].f].nxt[i]; 30 a[to].ed|=a[a[to].f].ed; 31 h.push(to); 32 }else to=a[a[x].f].nxt[i]; 33 } 34 } 35 } 36 37 struct mat{ 38 int A[205][205]; 39 mat(){memset(A,0,sizeof(A));} 40 mat operator * (mat &tmp) const{ 41 mat c;int w=sz<<1|1; 42 for(int i=0;i<=w;++i) 43 for(int j=0;j<=w;++j) 44 for(int k=0;k<=w;++k) 45 c.A[i][j]=(c.A[i][j]+1ll*A[i][k]*tmp.A[k][j]%mod)%mod; 46 return c; 47 } 48 }; 49 mat Pow(mat x,int y){ 50 mat rs; 51 for(int i=0;i<=sz;++i) 52 rs.A[i<<1][i<<1]=1; 53 for(;y;y>>=1,x=x*x) 54 if(y&1) rs=rs*x; 55 return rs; 56 } 57 58 void task1(){ 59 f[0][0]=1; 60 for(int i=0;i<L;++i) 61 for(int j=0;j<=sz;++j) 62 if(f[j][i]) 63 for(int k=1;k<=n;++k) 64 if(p[j][k]!=-1&&i+siz[k]<=L) 65 f[p[j][k]][i+siz[k]]=(f[p[j][k]][i+siz[k]]+f[j][i])%mod; 66 for(int i=0;i<=sz;++i) if(!a[i].ed) ans=(ans+f[i][L])%mod; 67 } 68 void task2(){ 69 mat res; 70 for(int i=0;i<=sz;++i){ 71 for(int j=1;j<=n;++j) 72 if(p[i][j]!=-1){ 73 if(siz[j]==1) ++res.A[p[i][j]<<1][i<<1]; 74 else ++res.A[p[i][j]<<1][i<<1|1]; 75 } 76 res.A[i<<1|1][i<<1]=1; 77 }res=Pow(res,L); 78 for(int i=0;i<=sz;++i) if(!a[i].ed) ans=(ans+res.A[i<<1][0])%mod; 79 } 80 int main(){ 81 memset(p,-1,sizeof(p)); 82 scanf("%d%d%d",&n,&m,&L); 83 for(int i=1;i<=n;++i) scanf("%s",s[i]),siz[i]=strlen(s[i]); 84 for(int i=1;i<=m;++i) add(); 85 acbuild(); 86 for(int i=1;i<=n;++i) 87 for(int j=0,u=0;j<=sz;u=++j){ 88 for(int k=0;k<siz[i]&&!a[u].ed;++k) 89 u=a[u].nxt[s[i][k]-'a']; 90 if(!a[u].ed) p[j][i]=u; 91 } 92 if(L<=100) task1(); 93 else task2(); 94 printf("%d",ans); 95 return 0; 96 }