题目大意:给出一些字符串。已知如果文章里出现过这些字符串中的一个,那么就说这个文章是可读的。问长度为l的文章有多少是可读的文章。
思路:直接处理不太好弄, 我们可以统计出来不可读的文章,然后用26^l减去就是可读的文章总数。
将所有的字串建Trie图,然后设f[i][j]为文章的第i个字符Trie图中的第j个节点的时候不可读的文章的数量。转移就很简单了。注意一下取模就行了。
CODE:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 10010 #define MO 10007 using namespace std; #define P(a) ((a) - 'A') struct Trie{ Trie *son[26],*fail; bool end,id; }mempool[MAX],*C = mempool,*root = C++; int cnt,length; char s[MAX]; int f[110][MAX];//表示匹配到第i位trie图中j时的种类数。显然f[0][0] = 1; inline void Insert(char *s) { Trie *now = root; while(*s != '\0') { if(now->son[P(*s)] == NULL) now->son[P(*s)] = C++; now = now->son[P(*s)]; ++s; } now->end = true; } void BuildTrieGraph() { static queue<Trie *> q; while(!q.empty()) q.pop(); for(int i = 0; i < 26; ++i) if(root->son[i] != NULL) root->son[i]->fail = root,q.push(root->son[i]); else root->son[i] = root; while(!q.empty()) { Trie *now = q.front(); q.pop(); for(int i = 0; i < 26; ++i) if(now->son[i] != NULL) { now->son[i]->fail = now->fail->son[i]; now->son[i]->end |= now->fail->son[i]->end; q.push(now->son[i]); } else now->son[i] = now->fail->son[i] ? now->fail->son[i]:root; } } int main() { cin >> cnt >> length; for(int i = 1; i <= cnt; ++i) { scanf("%s",s); Insert(s); } BuildTrieGraph(); f[0][0] = 1; for(int i = 0; i < length; ++i) for(int j = 0; j < C - mempool; ++j) for(int k = 0; k < 26; ++k) { Trie *now = mempool[j].son[k]; if(!now->end) f[i + 1][now - mempool] = (f[i + 1][now - mempool] + f[i][j]) % MO; } int ans = 0; for(int i = 0; i < C - mempool; ++i) ans = (ans + f[length][i]) % MO; int total = 1; for(int i = 1; i <= length; ++i) total = total * 26 % MO; cout << (total - ans + MO) % MO << endl; return 0; }
BZOJ 1030 JSOI 2007 文本生成器 AC自动机+DP
原文地址:http://blog.csdn.net/jiangyuze831/article/details/41804341