【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 } 

 

posted @ 2016-03-01 15:44  iiyiyi  阅读(340)  评论(0编辑  收藏  举报