随机生成一个字符可以看成在AC自动机里面向前走一个节点,那么ans就是0向前走L步并且不经过单词节点,
由概率知识可得,f[p][L]=∑f[nxt[p][i]][L-1]*g[i] 其中p表示位于p节点,还要走L步,边界是f[p][0]=1.0,可以用记忆化搜索
非常巧妙的做法
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<queue> #define MAXN 25*25+10 using namespace std; struct AC{ int nxt[MAXN][65]; int f[MAXN]; int match[MAXN]; int m; char ch[65]; double g[65]; int cnt; int vis[MAXN][MAXN]; double ans[MAXN][MAXN]; void init(){ memset(vis,0,sizeof(vis)); memset(nxt,0,sizeof(nxt)); memset(f,0,sizeof(f)); memset(match,0,sizeof(match)); cnt=0; } int dex(char c){ if('a'<=c&&c<='z')return c-97; if('A'<=c&&c<='Z')return c-65+26; return c-48+52; } void insert(char s[]){ int p=0; int len=strlen(s); for(int i=0;i<len;i++){ int t=dex(s[i]); if(!nxt[p][t]){ nxt[p][t]=(++cnt); } p=nxt[p][t]; } match[p]=1; } double work(int p,int L){ if(!L)return 1.0; if(vis[p][L])return ans[p][L]; vis[p][L]=1; double &ret=ans[p][L]; ret=0.0; for(int i=1;i<=m;i++){ int t=dex(ch[i]); if(!match[nxt[p][t]]){ ret+=g[i]*work(nxt[p][t],L-1); } } return ret; } void getFail(){ queue<int> q; for(int i=0;i<65;i++){ if(nxt[0][i]){ q.push(nxt[0][i]); } } while(!q.empty()){ int x=q.front();q.pop(); for(int i=0;i<65;i++){ int y=nxt[x][i]; if(!y){nxt[x][i]=nxt[f[x]][i];continue;} f[y]=nxt[f[x]][i]; match[y]|=match[f[y]]; q.push(y); } } } }A; int n,m,L; char s[105]; void solve(){ A.init(); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); A.insert(s); } A.getFail(); scanf("%d",&m); A.m=m; for(int i=1;i<=m;i++){ double t; scanf("%s%lf",s,&t); A.ch[i]=s[0]; A.g[i]=t; } scanf("%d",&L); printf("%.6f ",A.work(0,L)); } int main() { // freopen("data.in","r",stdin); int T; scanf("%d",&T); for(int i=1;i<=T;i++){ printf("Case #%d: ",i); solve(); } return 0; }