【JZOJ3472】匹配
【JZOJ3472】匹配
by AmanoKumiko
Description
给定k个字符串以及长度为n的母串可选字母的集合,问母串要完整出现给定的k个字符串的方案数,答案模1000000007,字符仅包含小写字母。
Input
第一行两个整数n、k,表示字符串的长度和给定字符串的个数。
接下来k行每行一个字符串。
接下来一行1个整数m表示可选字母集合内元素个数。
接下来一行给出一个长为m的字符串,表示字母的集合(可能有重复)。
Output
一个整数ans,表示方案数。
Sample Input
3 2
cr
rh
4
acrh
Sample Output
1
【样例解释】
只有crh符合。
Data Constraint
30%的数据n<=10,m<=3。
60%的数据n<=40。
另有10%的数据k=0。
另有10%的数据m=1。
100%的数据n<=100,m<=10,k<=8,给定字符串长度<=30。
Solution
AC自动机上dp套路题
1.对k个串建AC自动机
2.统计每个点可以匹配串的集合(状压)
3.预处理每个点加上一个字符后会去到的点
4.设\(f[i][j][S]\)表示放了\(i\)个字符,到了AC自动机上第\(j\)个点,已匹配的字符串集合为\(S\)的方案数,转移显然
Code
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define Fs(i,a) for(int i=last[a];i;i=e[i].next)
#define LL long long
#define mo 1000000007
#define P 250
#define N 110
#define K 15
#define L 30
int n,m,k,cnt,len[K],top,last[P],f[N][P][1<<8],ans;
struct node{int en,next;}e[P*2];
void add(int a,int b){e[++cnt]=(node){b,last[a]};last[a]=cnt;}
char s[K][L],ch[K],c[K];
bool vis[26];
queue<int>q;
struct ACM{
int son[P][26],pos[P][26],fail[P],tmp[P],p;
void insert(int x){
int u=1;
F(j,1,len[x])!son[u][s[x][j]-'a']?u=son[u][s[x][j]-'a']=++p:u=son[u][s[x][j]-'a'];
tmp[u]|=1<<x-1;
}
void build(){
F(i,0,25)son[0][i]=1;q.push(1);
while(!q.empty()){
int u=q.front();q.pop();add(fail[u],u);
F(i,0,25){
int v=son[u][i],Fail=fail[u];
if(!v){son[u][i]=son[Fail][i];continue;}
add(u,v);fail[v]=son[Fail][i];q.push(v);
}
}
}
void calc(){
F(i,1,p){
Fs(j,i)tmp[e[j].en]|=tmp[i];//匹配串的集合
F(j,0,25){int v=i;while(v&&!son[v][j])v=fail[v];pos[i][j]=max(son[v][j],1);}//加上一个字符后会去到的点
}
}
}t;
int main(){
t.p=1;
scanf("%d%d",&n,&k);
F(i,1,k)scanf("%s",s[i]+1),len[i]=strlen(s[i]+1),t.insert(i);
t.build();t.calc();
scanf("%d%s",&m,ch+1);
F(i,1,m)if(!vis[ch[i]-'a'])c[++top]=ch[i],vis[ch[i]-'a']=1;
f[0][1][0]=1;
F(i,0,n-1) F(j,1,t.p) F(S,0,(1<<k)-1)if(f[i][j][S]){
F(x,1,m){int y=t.pos[j][c[x]-'a'];if(y)(f[i+1][y][S|t.tmp[y]]+=f[i][j][S])%=mo;}
}
F(i,1,t.p)(ans+=f[n][i][(1<<k)-1])%=mo;//状压DP
printf("%d",ans);
return 0;
}