UVA 11468 Substring
题目大意:给出n种字符和其出现的概率,给出一些模板串,求随机生成长度为L的串中不含任意模板串作为子串的概率。模板串个数≤20,串长≤20,L≤100。
这么多个模板串肯定是AC自动机。
不出现任意一个模板串,即走不到AC自动机上val有值(标为单词结尾)的点。
随机生成长度为L的字符串,即在AC自动机上随机走L步。
处理方法是记忆化搜索。设g(x,step)表示当前在AC自动机的点x上、还需要走step步,走不到结尾点的概率。那么答案就是g(0,L)。
那么转移根据全概率公式(感性证明)就是
能转移当且仅当ch[x][i]不是一个串的结尾。
边界条件是g(x,0)=1。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define dob double #define FILE "11468" using namespace std; const int N = 1010; const int M = 71; int n,L,K,tot,ch[N][M],id[N],val[N],fail[N]; double P[N][N],pro[N]; bool vis[N][N]; char S[N][N],g[M]; inline int gi(){ int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline void insert(char *s,int rt=0){ for(int i=0,j=strlen(s);i<j;++i){ int x=id[s[i]]; if(!ch[rt][x]){ ch[rt][x]=++tot;val[tot]=0; memset(ch[tot],0,sizeof(ch[tot])); } rt=ch[rt][x]; } val[rt]=1; } inline void getfail(){ queue<int>Q;fail[0]=val[0]=0; for(int i=1;i<=n;++i){ int y=ch[0][i]; if(y)fail[y]=0,Q.push(y); } while(!Q.empty()){ int x=Q.front();Q.pop(); for(int i=1;i<=n;++i){ int y=ch[x][i]; if(!y){ ch[x][i]=ch[fail[x]][i]; continue; } Q.push(y);int z=fail[x]; while(z && ch[z][i]==0)z=fail[z]; fail[y]=ch[z][i];val[y]|=val[fail[y]]; } } } inline double getprob(int x,int step){ if(!step)return 1.0; if(vis[x][step])return P[x][step]; vis[x][step]=1;double ans=0.0; for(int i=1;i<=n;++i) if(!val[ch[x][i]]) ans+=pro[i]*getprob(ch[x][i],step-1); return P[x][step]=ans; } inline void solve(){ K=gi();tot=0; memset(ch[0],0,sizeof(ch[0])); memset(vis,0,sizeof(vis)); for(int i=1;i<=K;++i)scanf("%s",S[i]); n=gi();memset(id,0,sizeof(id)); for(int i=1;i<=n;++i){ scanf("%s%lf",g,&pro[i]); id[g[0]]=i; } for(int i=1;i<=K;++i)insert(S[i]); getfail();L=gi(); double ans=getprob(0,L); printf("%.6lf\n",ans); } int main() { freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); int Case=gi(); for(int t=1;t<=Case;++t){ printf("Case #%d: ",t); solve(); } fclose(stdin);fclose(stdout); return 0; }