【题意】

【解题方法】

详细可见白书P 218

构造完改造后的AC自动机后,每随机生成一个字母,相当于在AC自动机中随机走一步。所以有单词的节点标记为"禁止"。则本题就是求从节点0开始走L步,不进入任何禁止节点的概率。

        令d[i][j]表示当前在i节点,还有长为j的路要走且不经过单词节点的概率。初值为d[i][0]=1,其中i为非单词节点,否则d[i][0]=0。

        d[i][j] = sum( pro(x)*d[k][j-1] )。其中x表示从节点i往节点k走的那条边表示字符x,pro(x)是选择字符x的概率。 且k节点是非单词节点。所以利用记忆话搜索我们就可以计算出d[0][L]。

        代码中的match[i]表示节点i是否为单词节点 或 是否有单词正好是节点i的后缀。对于本题有了match数组就不必维护val和last数组了。

【AC 代码】

//
//Created by just_sort 2016/8/20
//All Rights Reserved
//

#include <map>
#include <queue>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 500;
const int maxs = 100;
int n,m,id[maxn];

struct acautomata{
    int nxt[maxn][maxs],fail[maxn],match[maxn];
    int sz,root;
    int newnode()
    {
        memset(nxt[sz],0,sizeof(nxt[sz]));
        match[sz]=0;
        return sz++;
    }
    void init()
    {
        sz = 0;
        root = newnode();
    }
    void insert(char *s){
        int u=root,n=strlen(s);
        for(int i=0; i<n; i++){
            int pos = id[s[i]];
            if(!nxt[u][pos]){
                nxt[u][pos] = newnode();
            }
            u = nxt[u][pos];
        }
        match[u] = 1;
    }
    void build()
    {
        queue<int>q;
        match[root]=fail[root]=root;
        int u;
        for(int i=0; i<n; i++){
            int &v = nxt[root][i];
            if(v){
                fail[v] = 0;
                q.push(v);
            }else{
                v = root;
            }
        }
        while(q.size()){
            u = q.front();q.pop();
            for(int i=0; i<n; i++){
                int &v = nxt[u][i];
                if(!v){
                    v = nxt[fail[u]][i];
                    continue;
                }
                q.push(v);
                fail[v] = nxt[fail[u]][i];
                match[v] |= match[fail[v]];
            }
        }
    }
}ac;
double dp[maxn][105];
bool  vis[maxn][105];
char  str[30][30];
double p[maxn];
double dfs(int u,int L)
{
    if(!L) return 1.0;
    if(vis[u][L]) return dp[u][L];
    vis[u][L]=1;
    double &ans = dp[u][L];
    ans = 0.0;
    for(int i=0; i<n; i++){
        if(!ac.match[ac.nxt[u][i]]) ans+=p[i]*dfs(ac.nxt[u][i],L-1);
    }
    return ans;
}
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        memset(dp,0,sizeof(dp));
        ac.init();
        scanf("%d",&m);
        for(int i=0; i<m; i++){
            scanf("%s",str[i]);
        }
        scanf("%d",&n);
        char s[20];
        for(int i=0; i<n; i++){
            scanf("%s %lf",s,&p[i]);
            id[s[0]] = i;
        }
        for(int i=0; i<m; i++){
            ac.insert(str[i]);
        }
        ac.build();
        int L;
        scanf("%d",&L);
        memset(vis,0,sizeof(vis));
        double ans = dfs(0,L);
        printf("Case #%d: %.6lf\n",cas++,ans);
    }
    return 0;
}