HDU2825
题目大意
- 给定?个单词,统计所有长度为?的包含至少?个单词的串的个数
- ? ≤ 25,? ≤ 10,单词长度 ≤ 10
分析
- 对读入的?个单词建出 AC 自动机
- 令??[?][?][????]代表长度为 ?,目前匹配到 AC 自动机上编号为? 的节点,已经包含的单词集合为????的方案数
- 假设我们目前匹配到 AC 自动机上的节点?,那么能够包含的单词是 从点?开始,沿着????指针一直到根路径上的所有单词
- 我们可以在构建 AC 自动机的 fail 指针时预处理出每个节点到根路径 上经过单词编号集合
- ??[?][?][????]⟶?? [?+1][?? ?,?][????|sum[?? ?,?]],?为枚举的 下一个字符
Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int mod=20090717;
const int N=500;
int tr[N][N],f[30][150][1500],end[N],sum[N],cnt[2000],fail[N],n,m,limit,ans,tot;
void init(){
memset(sum,0,sizeof(sum));
memset(fail,0,sizeof(fail));
memset(tr,0,sizeof(tr));
memset(f,0,sizeof(f));
f[0][0][0]=1;
ans=tot=0;
}
void insert(string s,int pos){
int now=0,len=s.size();
rep(i,0,len-1){
if(!tr[now][s[i]-'a'])tr[now][s[i]-'a']=++tot;/注意!变量值会发生变化//
now=tr[now][s[i]-'a'];
}
sum[now]=1<<pos;
}
void AC(){
queue<int> q;
rep(i,0,25)if(tr[0][i])q.push(tr[0][i]);
while(q.size()){
int x=q.front();q.pop();
rep(i,0,25){
if(!tr[x][i])tr[x][i]=tr[fail[x]][i];
else{
q.push(tr[x][i]);
fail[tr[x][i]]=tr[fail[x]][i];
}
sum[tr[x][i]]|=sum[tr[fail[x]][i]];/注意!变量值会发生变化//
}
}
}
int count(int S){
int num=0;
while(S){if(S&1)num++;S>>=1;}
return num;
}
int main()
{
rep(i,0,1023)cnt[i]=count(i);
while(~scanf("%d%d%d",&n,&m,&limit)&&(n||m||limit)){
init();
rep(i,0,m-1){
string s;
cin>>s;insert(s,i);
}
AC();//不要忘了构造fail指针
for(int i=0;i<=n;i++){
for(int j=0;j<=tot;j++){
for(int k=0;k<(1<<m);k++){
if(!f[i][j][k])continue;
for(int t=0;t<26;t++){
int y=tr[j][t];
f[i+1][y][k|sum[y]]=(f[i+1][y][k|sum[y]]+f[i][j][k])%mod;
}
}
}
}
rep(j,0,tot)rep(k,0,(1<<m)-1)if(cnt[k]>=limit)ans=(ans+f[n][j][k])%mod;
cout<<ans<<"\n";
}
return 0;
}