题目描述
JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版。
该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。ZYX需要指出GW文本生成器 v6
生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助吗?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
trie树上面跑dp。用ac自动机或者trie图建立fail指针,然后转移就行了。
重点是要确定某个点能不能跑到。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MOD 10007 #define ll long long int n,m; char s[70][150]; int len[70],tot; struct Trie { int ch[28]; int f; int w; }tr[7500]; queue<int>q; void trie_pic() { for(int i=1;i<=26;i++) if(tr[0].ch[i]) q.push(tr[0].ch[i]); while(!q.empty()) { int u = q.front(); q.pop(); for(int i=1;i<=26;i++) { int &v = tr[u].ch[i]; if(!v) { v = tr[tr[u].f].ch[i]; continue; } tr[v].f = tr[tr[u].f].ch[i]; tr[v].w|= tr[tr[v].f].w; q.push(v); } } } int dp[7500][150]; ll fast(ll x,int y) { int ret = 1; while(y) { if(y&1)ret = (ret*x)%MOD; x = (x*x)%MOD; y>>=1; } return ret; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s1",s[i]+1); len[i] = strlen(s[i]+1); int u = 0; for(int j=1;j<=len[i];j++) { int c = s[i][j]-'A'+1; if(!tr[u].ch[c])tr[u].ch[c]=++tot; u=tr[u].ch[c]; } tr[u].w = 1; } trie_pic(); int ans = (int)fast(26,m); dp[0][0]=1; for(int k = 0;k<m;k++) { for(int i=0;i<=tot;i++) { if(dp[i][k]) { for(int j=1;j<=26;j++) { if(!tr[tr[i].ch[j]].w) { (dp[tr[i].ch[j]][k+1]+=dp[i][k])%=MOD; } } } } } for(int i=0;i<=tot;i++) { (ans-=dp[i][m])%=MOD; } printf("%d ",(ans%MOD+MOD)%MOD); return 0; }