原文链接http://www.cnblogs.com/zhouzhendong/p/8196279.html
题目传送门 - BZOJ2553
题意概括
引用一下lych大佬的:
在字母只有前alphabet时,给定N个串,求长度为len的串包含这些N个串的个数最大值的期望值。
题解
我们首先发现总共的字符个数才就75个,那么闭着眼睛先建一个AC自动机,Trie图建好。反正代码不长。
然后我们发现长度很大,显然就是要往矩阵快速幂那里考虑。
我们可以用矩阵来快速计算到达AC自动机的每一个位置的概率。
然后我们考虑原问。
对于一个串,把它划分成包含最多的禁忌串数的方案怎么做?
我们可以贪心的来,从头开始沿着字符串在AC自动机上面走,每一次到一个结束点就划分。这样一定是最优的。
然后回到矩阵中。
假如说我们考虑DP的话,那么每到一个结束点,都要统计一下当前概率对答案的贡献,就是概率*1
那么我们可以把它记入答案中。
同理,我们可以在矩阵中多开一列,用来维护答案。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> using namespace std; typedef long double LD; const int S=80; struct Trie{ int fail,e,Next[26]; void clear(){ fail=e=0; memset(Next,0,sizeof Next); } }tree[S]; int n,len,alpha,cnt; void build(char ch[]){ int rt=1,t,len=strlen(ch); for (int i=0;i<len;i++){ t=ch[i]-'a'; if (!tree[rt].Next[t]){ tree[++cnt].clear(); tree[rt].Next[t]=cnt; } rt=tree[rt].Next[t]; } tree[rt].e=1; } void build_AC(){ int q[S],head=0,tail=0; int rt,son,k; q[++tail]=1,tree[0].fail=1; while (head<tail){ int rt=q[++head]; for (int i=0;i<alpha;i++){ son=tree[rt].Next[i]; if (!son){ tree[rt].Next[i]=tree[tree[rt].fail].Next[i]; continue; } k=tree[rt].fail; while (!tree[k].Next[i]) k=tree[k].fail; tree[son].fail=tree[k].Next[i]; tree[son].e|=tree[tree[k].Next[i]].e; q[++tail]=son; } } } int m; struct Mat{ LD v[S][S]; Mat (){} Mat (int x){set(x);} void print(){ for (int i=1;i<=m;i++,puts("")) for (int j=1;j<=m;j++) printf("%5.3Lf ",v[i][j]); puts(""); } void set(int x){ memset(v,0,sizeof v); if (x==1) for (int i=1;i<=m;i++) v[i][i]=1; } Mat operator * (Mat b){ Mat ans(0); for (int i=1;i<=m;i++) for (int j=1;j<=m;j++) for (int k=1;k<=m;k++) ans.v[i][j]+=v[i][k]*b.v[k][j]; return ans; } Mat operator ^ (int y){ Mat ans(1),x=*this; while (y){ if (y&1) ans=ans*x; x=x*x; y>>=1; } return ans; } }M(0); char s[S]; int main(){ scanf("%d%d%d",&n,&len,&alpha); cnt=1; tree[0].clear(); tree[1].clear(); for (int i=0;i<alpha;i++) tree[0].Next[i]=1; for (int i=1;i<=n;i++){ scanf("%s",s); build(s); } build_AC(); m=cnt+1; LD addv=1.0/(1.0*alpha); for (int i=1;i<=cnt;i++) for (int j=0;j<alpha;j++) if (tree[tree[i].Next[j]].e) M.v[i][1]+=addv,M.v[i][m]+=addv; else M.v[i][tree[i].Next[j]]+=addv; M.v[m][m]=1; Mat Mans=M^len; printf("%.10lf",(double)Mans.v[1][m]); return 0; }