題目大意:給出n種字符和其出現的機率,給出一些模板串,求隨機生成長度爲L的串中不含任意模板串做爲子串的機率。模板串個數≤20,串長≤20,L≤100。 ios
這麼多個模板串確定是AC自動機。ide
不出現任意一個模板串,即走不到AC自動機上val有值(標爲單詞結尾)的點。spa
隨機生成長度爲L的字符串,即在AC自動機上隨機走L步。code
處理方法是記憶化搜索。設g(x,step)表示當前在AC自動機的點x上、還須要走step步,走不到結尾點的機率。那麼答案就是g(0,L)。blog
那麼轉移根據全機率公式(感性證實)就是字符串
能轉移當且僅當ch[x][i]不是一個串的結尾。get
邊界條件是g(x,0)=1。string
#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; }