BZOJ2553 [BeiJing2011]禁忌 AC自动机 矩阵
原文链接http://www.cnblogs.com/zhouzhendong/p/8196279.html
题目传送门 - BZOJ2553
题意概括
引用一下lych大佬的:
在字母只有前alphabet时,给定N个串,求长度为len的串包含这些N个串的个数最大值的期望值。
题解
我们首先发现总共的字符个数才就75个,那么闭着眼睛先建一个AC自动机,Trie图建好。反正代码不长。
然后我们发现长度很大,显然就是要往矩阵快速幂那里考虑。
我们可以用矩阵来快速计算到达AC自动机的每一个位置的概率。
然后我们考虑原问。
对于一个串,把它划分成包含最多的禁忌串数的方案怎么做?
我们可以贪心的来,从头开始沿着字符串在AC自动机上面走,每一次到一个结束点就划分。这样一定是最优的。
然后回到矩阵中。
假如说我们考虑DP的话,那么每到一个结束点,都要统计一下当前概率对答案的贡献,就是概率*1
那么我们可以把它记入答案中。
同理,我们可以在矩阵中多开一列,用来维护答案。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #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; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术