f(i,j,S)表示当前字符串总长度为i,dp到AC自动机第j个结点,单词集合为S时的方案数。
要注意有点卡常数,注意代码里的注释。
#include<cstdio> #include<cstring> #include<queue> using namespace std; queue<int>q; #define MOD 20090717; int child[111][26],fail[111],size,f[30][111][1100],tag[111]; void Insert(char S[],int id) { int len=strlen(S); int now=0; for(int i=0;i<len;++i) { if(!child[now][S[i]-‘a‘]) child[now][S[i]-‘a‘]=size++; now=child[now][S[i]-‘a‘]; } tag[now]|=(1<<id); } void build() { fail[0]=-1; q.push(0); while(!q.empty()) { int U=q.front(); q.pop(); for(int i=0;i<26;++i) if(child[U][i]) { int V=fail[U]; while(V!=-1) { if(child[V][i]) { fail[child[U][i]]=child[V][i]; break; } V=fail[V]; } if(V==-1) fail[child[U][i]]=0; tag[child[U][i]]|=tag[fail[child[U][i]]]; q.push(child[U][i]); } else if(U) child[U][i]=child[fail[U]][i]; } } void Init() { memset(child,0,sizeof(child)); memset(fail,0,sizeof(fail)); memset(tag,0,sizeof(tag)); size=1; } int n,m,K; bool check(int x) { int res=0; for(int i=0;i<m;++i) res+=((x>>i)&1); return res>=K; } int main() { //freopen("hdu2825.in","r",stdin); char s[13]; while(1) { scanf("%d%d%d",&n,&m,&K); if(n==0 && m==0 && K==0) break; Init(); for(int i=0;i<m;++i) { scanf("%s",s); Insert(s,i); } build(); for(int i=0;i<=n;++i) for(int j=0;j<size;++j) for(int k=0;k<(1<<m);++k) f[i][j][k]=0;//用memset也许会TLE f[0][0][0]=1; for(int i=0;i<n;++i) for(int j=0;j<size;++j) for(int k=0;k<(1<<m);++k) { if(!f[i][j][k])//不加会TLE continue; for(int l=0;l<26;++l) f[i+1][child[j][l]][k|tag[child[j][l]]]= (f[i+1][child[j][l]][k|tag[child[j][l]]]+f[i][j][k])%MOD; } int ans=0; for(int i=0;i<(1<<m);++i) if(check(i)) for(int j=0;j<size;++j) ans=(ans+f[n][j][i])%MOD; printf("%d\n",ans); } return 0; }
时间: 2024-10-29 19:06:10