[JSOI2007]文本生成器
题目大意:
给定$n(n\le60)$个字符串$s_i(|s_i|\le100)$,问有多少个长度为$m(m\le100)$的字符串$t$满足至少包含一个$s_i$?保证所有字符串均由大写英文字母构成。
思路:
建立AC自动机。用$f[i][j]$表示构造到$t$的第$i$位,对应自动机上编号为$j$的结点,不包含任何$s_i$的方案数。显然遇到危险结点需要跳过,否则直接转移即可。答案即为$26^m-\sum f[m][i]$。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 inline int getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int N=60,L=101,S=26,mod=10007; 12 char s[L]; 13 int f[L][N*L]; 14 class AhoCorasick { 15 private: 16 bool val[N*L]; 17 std::queue<int> q; 18 int ch[N*L][S],fail[N*L]; 19 int sz,new_node() { 20 return ++sz; 21 } 22 int idx(const int &c) const { 23 return c-'A'; 24 } 25 public: 26 void insert(const char s[]) { 27 int p=0; 28 for(register int i=0;s[i];i++) { 29 const int c=idx(s[i]); 30 p=ch[p][c]?:ch[p][c]=new_node(); 31 } 32 val[p]=true; 33 } 34 void get_fail() { 35 for(register int c=0;c<S;c++) { 36 if(ch[0][c]) q.push(ch[0][c]); 37 } 38 while(!q.empty()) { 39 const int &x=q.front(); 40 for(register int c=0;c<S;c++) { 41 int &y=ch[x][c]; 42 if(!y) { 43 y=ch[fail[x]][c]; 44 continue; 45 } 46 fail[y]=ch[fail[x]][c]; 47 val[y]|=val[fail[y]]; 48 q.push(y); 49 } 50 q.pop(); 51 } 52 } 53 int solve(const int &n,const int &m) const { 54 int ret=f[0][0]=1; 55 for(register int i=1;i<=m;i++) { 56 (ret*=S)%=mod; 57 for(register int j=0;j<=sz;j++) { 58 if(!f[i-1][j]) continue; 59 for(register int c=0;c<S;c++) { 60 if(!val[ch[j][c]]) (f[i][ch[j][c]]+=f[i-1][j])%=mod; 61 } 62 } 63 } 64 for(register int i=0;i<=sz;i++) { 65 ret=(ret-f[m][i]+mod)%mod; 66 } 67 return ret; 68 } 69 }; 70 AhoCorasick ac; 71 int main() { 72 const int n=getint(),m=getint(); 73 for(register int i=0;i<n;i++) { 74 scanf("%s",s); 75 ac.insert(s); 76 } 77 ac.get_fail(); 78 printf("%d\n",ac.solve(n,m)); 79 return 0; 80 }