题目大意:给出一些字符串。已知如果文章里出现过这些字符串中的一个,那么就说这个文章是可读的。问长度为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;
}