题意:给m个字符串,求长为n且至少包含k个上述字符串的字符串有多少个。
数据范围:(1<=n<=25),(0<=m<=10)
分析:用dp[i][cur][s]表示走i步后,到达结点cur,包含的字符串压缩为状态s(第x位为1表示包含第x个字符串)的字符串有多少个。
TLE:当dp[i-1][pre][s]=0时,直接continue,不用进入下一层循环,经此优化后就AC了。
View Code
#include <stdio.h> #include <string.h> #include <queue> using namespace std; #define NODE 101 #define MOD 20090717 int n,m,cnt; int next[NODE][26],fail[NODE],flag[NODE],node; int dp[26][NODE][1<<10]; int newnode() { fail[node]=flag[node]=0; memset(next[node],0,sizeof(next[0])); return node++; } void insert(char *s,int id) { int i,k,cur; for(i=cur=0;s[i];i++) { k=s[i]-'a'; if(!next[cur][k]) next[cur][k]=newnode(); cur=next[cur][k]; } flag[cur] |=(1<<id); } void makenext() { int u,v; queue<int>q; q.push(0); while(!q.empty()) { u=q.front(),q.pop(); for(int k=0;k<26;k++) { v=next[u][k]; if(v) q.push(v); else next[u][k]=next[fail[u]][k]; if(u&&v) { fail[v]=next[fail[u]][k]; flag[v] |=flag[fail[v]]; } } } } int cal(int s) { int ret=0; while(s) { ret+=s&1; s>>=1; } return ret; } void DP() { for(int i=0;i<=n;i++) { for(int j=0;j<node;j++) memset(dp[i][j],0,sizeof(dp[0][0][0])*(1<<m)); } dp[0][0][0]=1; for(int i=1;i<=n;i++) { for(int pre=0;pre<node;pre++) { for(int s=0;s<(1<<m);s++) { if(dp[i-1][pre][s]==0) continue; for(int k=0;k<26;k++) { int cur=next[pre][k]; int ns=s | flag[cur]; dp[i][cur][ns]+=dp[i-1][pre][s]; dp[i][cur][ns]%=MOD; } } } } int ans=0; for(int cur=0;cur<node;cur++) { for(int s=0;s<(1<<m);s++) { if(cal(s)>=cnt) { ans+=dp[n][cur][s]; ans%=MOD; } } } printf("%d\n",ans); } int main() { char s[11]; while(scanf("%d%d%d",&n,&m,&cnt)) { if(!n&&!m&&!cnt) break; node=0,newnode(); for(int i=0;i<m;i++) { scanf("%s",s); insert(s,i); } makenext(); DP(); } return 0; }