【Trie图+DP】BZOJ1030[JSOI2007]-文本生成器
【题目大意】
给出单词总数和固定的文章长度M,求出至少包含其中一个单词的可能文章数量。
【思路】
对于至少包含一个的类型,我们可以考虑补集。也就是等于[总的文章可能性总数-不包含任意一个单词的文章总数]有两个注意点:
1.Trie图+DP。Trie图和AC自动机的区别在于,当孩子i为NULL时,则让孩子指针等于fail指针的孩子i,这样就可以继续匹配下去了。因此寻找fail指针的时候,可以不用循环而用判断语句即可。
2.danger表示当前位置包含了单词,所以DP的时候舍去。如果你指向的fail指针是danger的,也就是你的后缀是danger的,那么当前的也是danger的。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 const int MAXN=100+50; 8 const int MAXM=60+5; 9 const int MOD=10007; 10 const int NUMA=26; 11 int n,m,cnt=0; 12 struct ACauto 13 { 14 int id; 15 int danger; 16 ACauto* next[NUMA]; 17 ACauto* fail; 18 ACauto() 19 { 20 danger=0; 21 id=++cnt; 22 for (int i=0;i<NUMA;i++) next[i]=NULL; 23 fail=NULL; 24 } 25 }; 26 ACauto* node[MAXN*MAXM]; 27 int f[MAXN][MAXM*MAXN]; 28 29 void insert(ACauto* root,char* str) 30 { 31 int len=strlen(str); 32 ACauto* tmp=root; 33 34 for (int i=0;i<len;i++) 35 { 36 int index=str[i]-'A'; 37 if (tmp->next[index]==NULL) 38 { 39 tmp->next[index]=new ACauto; 40 node[cnt]=tmp->next[index]; 41 } 42 tmp=tmp->next[index]; 43 } 44 tmp->danger=1; 45 } 46 47 void build(ACauto* root) 48 { 49 queue<ACauto*> que; 50 que.push(root); 51 while (!que.empty()) 52 { 53 ACauto* head=que.front();que.pop(); 54 for (int i=0;i<NUMA;i++) 55 { 56 if (head->next[i]==NULL) 57 { 58 if (head==root) head->next[i]=root; 59 else head->next[i]=head->fail->next[i]; 60 } 61 else 62 { 63 if (head==root) head->next[i]->fail=root; 64 else 65 { 66 head->next[i]->fail=head->fail->next[i]; 67 if (head->next[i]->fail->danger) head->next[i]->danger=1;/*注意!*/ 68 } 69 que.push(head->next[i]); 70 } 71 } 72 } 73 } 74 75 void dp(ACauto* root) 76 { 77 memset(f,0,sizeof(f)); 78 f[0][1]=1; 79 for (int i=0;i<=m-1;i++) 80 for (int j=1;j<=cnt;j++) 81 { 82 if (!node[j]->danger && f[i][j]) 83 { 84 for (int k=0;k<NUMA;k++)//枚举下一个字母 85 if (!node[j]->next[k]->danger) 86 f[i+1][node[j]->next[k]->id]=(f[i][j]+f[i+1][node[j]->next[k]->id])%MOD; 87 } 88 } 89 } 90 91 void findres() 92 { 93 int ans1=0,ans2=1; 94 for (int i=1;i<=cnt;i++) 95 if (!node[i]->danger) ans1=(ans1+f[m][i])%MOD; 96 for (int i=1;i<=m;i++) ans2=(ans2*NUMA)%MOD; 97 cout<<(ans2-ans1+MOD)%MOD<<endl; 98 } 99 100 int main() 101 { 102 char str[MAXN]; 103 ACauto* root=new ACauto; 104 node[1]=root; 105 scanf("%d%d",&n,&m); 106 for (int i=0;i<n;i++) 107 { 108 scanf("%s",str); 109 insert(root,str); 110 } 111 112 113 build(root); 114 dp(root); 115 findres(); 116 return 0; 117 }