Description

【GDOI2017第三轮模拟day2】魔法咒语(AC自动机,矩阵乘法)_AC自动机


【GDOI2017第三轮模拟day2】魔法咒语(AC自动机,矩阵乘法)_AC自动机_02

Solution

这道题目的60分非常的简单,直接用ac自动机直接搞一搞就好了。
但是后面的40分怎么做?
我们发现后面的40分全部都是基本单词长度不会大于2,所以我们可以考虑一下怎么矩阵乘法。
假设我们的转移的矩阵有两个部分:[i-1,i]
那么我们要转移到[i,i+1]
当单词长度为1的时候,可以从i转移到i+1,i-1转移到i
当单词长度为2的识货,可以从i-1转移到i+1
所以我们可以这样构造矩阵:
[空][i-1->i+1]
[i->i][i->i+1]
那么i->i就是对角线为1,i->i+1就是ac自动机转移式单词长度为1的部分,i-1->i+1就是ac自动机转移式单词长度为2的部分

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=207,mxx=1e4+7,mo=1e9+7;
char s[maxn];
int i,j,k,l,n,m,num,q,da;
int len[maxn],t[mxx][26],p[mxx],g[maxn];
int d[mxx],root,x,y,z,a[maxn][maxn];
ll ans;
struct node{
ll a[maxn][maxn];
}b,c,e,o,u;
ll f[maxn/2][mxx];
bool bz;
void bfs(){
int head=0,tail=1,i,j;
while(head<tail){
x=d[++head];
fo(i,0,25){
y=t[x][i];if(!y)continue;
z=p[x];g[y]+=g[x];
while(z&&!t[z][i])z=p[z];
if(z!=x)p[y]=t[z][i];g[y]+=g[p[y]];d[++tail]=y;
}
}
}
void cheng(node x,node y){
int i,j,k;
memset(u.a,0,sizeof(u.a));
fo(i,0,da){
fo(j,0,da){
fo(k,0,da)u.a[i][j]=(u.a[i][j]+x.a[i][k]*y.a[k][j]%mo)%mo;
}
}
o=u;
}
void qsm(node x,ll y){
int i,j;
fo(i,0,da)e.a[i][i]=1;
for(;y;y/=2){
if(y&1)cheng(e,x),e=o;
cheng(x,x);x=o;
}
}
int main(){
freopen("sorcery.in","r",stdin);
freopen("sorcery.out","w",stdout);
scanf("%d%d%d",&n,&m,&l);
fo(i,1,n){
scanf("%s",s+1);len[i]=strlen(s+1);
fo(j,1,len[i])a[i][j]=s[j]-'a';
}
fo(i,1,m){
scanf("%s",s+1);x=0;
fo(j,1,strlen(s+1)){
if(!t[x][s[j]-'a'])t[x][s[j]-'a']=++num;
x=t[x][s[j]-'a'];
}
g[x]++;
}
bfs();
if(l<=100){
f[0][0]=1;
fo(i,0,l-1){
fo(j,0,num){
if(!f[i][j])continue;
fo(k,1,n){
if(i+len[k]>l)continue;
x=j;bz=1;
fo(q,1,len[k]){
while(x&&!t[x][a[k][q]])x=p[x];y=t[x][a[k][q]];
if(g[y]){bz=0;break;}x=y;
}
if(bz)f[i+len[k]][x]=(f[i][j]+f[i+len[k]][x])%mo;
}
}
}
fo(j,0,num)ans=(ans+f[l][j])%mo;
printf("%d\n",ans);
}
else{
b.a[0][num+1]=1;
fo(j,0,num){
fo(k,1,n){
x=j;bz=1;
fo(q,1,len[k]){
while(x&&!t[x][a[k][q]])x=p[x];y=t[x][a[k][q]];
if(g[y]){bz=0;break;}x=y;
}
if(bz){
if(len[k]==1)c.a[j+num+1][x+num+1]++;
else c.a[j][x+num+1]++;
}
}
}
fo(j,0,num)c.a[j+num+1][j]=1;
da=num*2+1;
qsm(c,l);
c=e;cheng(b,c);
fo(j,num+1,2*num+1)ans=(ans+o.a[0][j])%mo;
printf("%lld\n",ans);
}
}