dtoj1721. 字符串生成器 ( strgen )
1721. 字符串生成器 ( strgen )
有一个字符串生成器,初始时生成的字符串为空串,它每次按照给定概率随机生成一个小写字母,加在当前已生成字符串的后面。
给定N个长度为L的字符串,每个字符串由小写字母组成。
如果在某个时候,发现每个给定字符串都在当前已生成的字符串中作为子串出现过,生成器就会停下来,将当前生成的字符串作为输出。
求输出字符串长度的期望值。
Sol
考虑AC自动机。
我们设f[k][S]表示当前走到的节点是k,已经完成的串为集合S,到最终状态的期望步数。
那么可以发现f[k][S]是若干方程。
我们按S分层,那么层与层之间的边是单向的,只有S连向S的子集。
那么我们可以一层层高斯消元,对于跨层的边我们先算出上一层的答案,这层中当成常数就行。
这题卡精!!!

#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define maxn 55 #define eps 1e-7 using namespace std; int T,n,l,c,tr[maxn][10],tot,fail[maxn]; int id[maxn]; double p[10],a[maxn][maxn],f[maxn][1<<10],ans[maxn]; char ch[maxn]; void Q(){ for(int i=0;i<=tot;i++){ fail[i]=0;id[i]=0; for(int j=0;j<c;j++)tr[i][j]=0; for(int j=0;j<(1<<n);j++)f[i][j]=0; } tot=0; } bool ins(int x){ int k=0; for(int i=0;i<l;i++){ if(!tr[k][ch[i]-'a'])tr[k][ch[i]-'a']=++tot; k=tr[k][ch[i]-'a']; } if(id[k])return 0; id[k]=1<<(x-1);return 1; } queue<int>q; void build(){ for(int i=0;i<c;i++)if(tr[0][i])q.push(tr[0][i]); while(!q.empty()){ int k=q.front();q.pop(); for(int i=0;i<c;i++){ if(tr[k][i])fail[tr[k][i]]=tr[fail[k]][i],q.push(tr[k][i]); else tr[k][i]=tr[fail[k]][i]; } } } void Guess(){ int N=tot; for(int i=0;i<=N;i++){ int Max=i; //for(int j=i;j<=N;j++)if(fabs(a[j][i])>fabs(a[Max][i]))Max=j; for(int j=i;j<=N+1;j++)swap(a[i][j],a[Max][j]); if(fabs(a[i][i])<eps)continue; for(int j=i+1;j<=N;j++){ double tmp=a[j][i]/a[i][i]; for(int k=i;k<=N+1;k++)a[j][k]-=a[i][k]*tmp; } } for(int i=N;i>=0;i--){ ans[i]=-a[i][N+1]/a[i][i]; for(int j=i-1;j>=0;j--){ a[j][N+1]+=a[j][i]*ans[i]; } } } void work(){ Q(); scanf("%d%d%d",&n,&l,&c); for(int i=1;i<=n;i++){ scanf(" %s",ch); if(!ins(i))n--,i--; } build(); for(int i=0,t;i<c;i++){ scanf("%d",&t);p[i]=1.0*t/10000; } int N=(1<<n)-1; for(int S=N-1;S>=0;S--){ for(int i=0;i<=tot;i++) for(int j=0;j<=tot+1;j++)a[i][j]=0; for(int i=0;i<=tot;i++){ for(int j=0;j<c;j++){ int v=tr[i][j]; a[i][v]+=p[j]; if(id[v]&&(!(S&id[v]))){ a[i][v]-=p[j]; a[i][tot+1]+=(f[v][S|id[v]])*p[j]; } } a[i][i]--;a[i][tot+1]++; } Guess(); for(int i=0;i<=tot;i++)f[i][S]=ans[i]; } printf("%.5lf\n",f[0][0]); } int main(){ for(scanf("%d",&T);T--;work()); return 0; }
分类:
字符串
, 字符串---AC自动机
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构