bzoj1030[JSOI2007]文本生成器
题意:
给出一个字典和一个长度,要求有多少个这个长度的字符串里含有子串为字典里的单词。字符串和字典里的字符都为大写字母。单词数≤60,字符串及单词长度≤100。
题解:
在AC自动机上跑dp,求不含字典单词的个数,再用总个数减。f[i][j]表示当前处理第i个位置,在trie上的节点为j。f[i][j]=sum{f[i+1][ch[x][k]]},x为j的fail祖先,为k为大写字母,不能走到val为1的节点。注意val的处理,特别是getfail中的“if(val[ch[y][i]])val[ch[x][i]]=1;”一句。
代码:
1 #include <cstring> 2 #include <cstdio> 3 #include <algorithm> 4 #include <queue> 5 #define maxn 10000 6 #define inc(i,j,k) for(int i=j;i<=k;i++) 7 #define mod 10007 8 using namespace std; 9 10 int n,m,sz,ch[maxn][26],fail[maxn],ans,f[150][maxn]; bool val[maxn]; 11 void insert(char *s){ 12 int x=0,len=strlen(s+1); inc(i,1,len){if(!ch[x][s[i]-'A'])ch[x][s[i]-'A']=++sz; x=ch[x][s[i]-'A'];} 13 val[x]=1; 14 } 15 queue <int> q; 16 void getfail(){ 17 inc(i,0,25)if(ch[0][i])q.push(ch[0][i]),fail[ch[0][i]]=0; 18 while(!q.empty()){ 19 int x=q.front(); q.pop(); 20 inc(i,0,25)if(ch[x][i]){ 21 int y=fail[x]; while(y&&!ch[y][i])y=fail[y]; if(ch[y][i])fail[ch[x][i]]=ch[y][i]; 22 if(val[ch[y][i]])val[ch[x][i]]=1; q.push(ch[x][i]); 23 } 24 } 25 } 26 char str[150]; 27 int main(){ 28 scanf("%d%d",&n,&m); inc(i,1,n)scanf("%s",str+1),insert(str); ans=1; getfail(); 29 inc(i,1,m)ans=ans*26%mod; f[0][0]=1; 30 inc(i,1,m)inc(j,0,sz)if(!val[j]&&f[i-1][j]){ 31 inc(k,0,25){ 32 int x=j; while(x&&!ch[x][k])x=fail[x]; f[i][ch[x][k]]=(f[i][ch[x][k]]+f[i-1][j])%mod; 33 } 34 } 35 inc(i,0,sz)if(!val[i]){ 36 ans=(ans-f[m][i])%mod; if(ans<0)ans+=mod; 37 } 38 printf("%d",ans); return 0; 39 }
20160621