链接:点击打开链接
题意:求一个长度是n的字符串至少含有给出的m个字符串中的k个的种数
代码:
#include <queue> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; const int MOD=20090717; int dp[30][105][(1<<10)+5]; int fail[100005],vis[100005]; int str[100005][30],dis[100005]; int root,id; void insert(char *s){ int u=0; for(;*s;s++){ if(!str[u][*s-'a']) str[u][*s-'a']=root++; u=str[u][*s-'a']; } dis[u]|=(1<<(id-1)); //用二进制表示每个状态 id++; } void getfail(){ int u,v,i,temp; queue<int>q; q.push(0); while(q.size()){ u=q.front();q.pop(); for(i=0;i<26;i++){ if(!str[u][i]) str[u][i]=str[fail[u]][i]; else { temp=str[u][i]; if(u) fail[temp]=str[fail[u]][i]; dis[temp]|=dis[fail[temp]]; //整合每个状态 q.push(temp); } } } } // 自动机模板 int main(){ //dp[i][j][k]表示长度是i的串匹配到j节点状态是k的值 int n,m,k,i,j,x,y,z,ans; char s[15]; while(scanf("%d%d%d",&n,&m,&k)!=EOF&&(n||m||k)){ root=id=1; memset(str,0,sizeof(str)); memset(dis,0,sizeof(dis)); memset(fail,0,sizeof(fail)); for(i=0;i<m;i++){ scanf("%s",s); insert(s); } getfail(); for(x=0;x<=n;x++) for(y=0;y<root;y++) for(z=0;z<(1<<m);z++) dp[x][y][z]=0; dp[0][0][0]=1; for(x=0;x<n;x++) for(y=0;y<root;y++) for(z=0;z<(1<<m);z++) if(dp[x][y][z]){ for(j=0;j<26;j++){ dp[x+1][str[y][j]][z|dis[str[y][j]]]+=dp[x][y][z]; dp[x+1][str[y][j]][z|dis[str[y][j]]]%=MOD; } } ans=0; for(i=0;i<(1<<m);i++) if(__builtin_popcount(i)>=k){ //个数不少于k for(j=0;j<root;j++) ans=(ans+dp[n][j][i])%MOD; } printf("%d\n",ans); } return 0; }