UVa11468

来源:互联网 发布:free mobile java 编辑:程序博客网 时间:2024/06/11 16:13

题目链接

简介:
给出一些字符和各自出现的概率,随机选择L次后将得到一个长度为L的随机字符串S,
再给出K个模式串,计算S中不含任何一个模式串的概率

分析:
这道题就是文本生成器的变式
我们先把所有串扔到一个AC自动机上所有单词结尾都设成false(不能到达)
在建立fail指针的时候,用失配的可到达性维护结点的可到达性

ed[ch[now][i]]|=ed[fail[ch[now][i]]];    //儿子的ed  儿子fail的ed

之后就是记忆化搜索版本的dp
每随机生成一个字母,相当于在AC自动机上走了一步
设f(i,j)表示当前结点为i,还需要走j步
转移方程:f(i,j)=sum{P[i]*f(k,j-1)|k是i的后继且可到达结点}

tip

本题的字典是给出的
所以我们需要一个map映射一下,构建AC自动机上以及dp都是在新的字典的基础上

数组要开够

//这里写代码片#include<cstdio>#include<cstring>#include<iostream>#include<queue>#include<map>using namespace std;int ch[405][70];int K,tot,fail[405],n,m;char s[21][21];bool ed[405],vis[405][105];double P[70],f[405][105];map<char,int> mp;void insert(char *s){    int now=0;    int len=strlen(s);    for (int i=0;i<len;i++)    {        int x=mp[s[i]];        if (!ch[now][x]) ch[now][x]=++tot;        now=ch[now][x];    }    ed[now]=1;}void make(){    queue<int> Q;    for (int i=0;i<m;i++)        if (ch[0][i]) Q.push(ch[0][i]);    while (!Q.empty())    {        int now=Q.front(); Q.pop();        for (int i=0;i<m;i++)        {            if (!ch[now][i])            {                ch[now][i]=ch[fail[now]][i];                continue;            }            fail[ch[now][i]]=ch[fail[now]][i];            ed[ch[now][i]]|=ed[fail[ch[now][i]]];    //儿子的ed  儿子fail的ed             Q.push(ch[now][i]);                      //容易漏写         }    }}double doit(int u,int L){    if (!L) return 1.0;    if (vis[u][L]) return f[u][L];    vis[u][L]=1;    double &ans=f[u][L];    ans=0.0;    for (int i=0;i<m;i++)              //m        if (!ed[ch[u][i]])            ans+=P[i]*doit(ch[u][i],L-1);    return ans;}int main(){    int T;    scanf("%d",&T);    for (int cas=1;cas<=T;cas++)    {        tot=0;        memset(ed,0,sizeof(ed));        memset(vis,0,sizeof(vis));        memset(ch,0,sizeof(ch));        memset(fail,0,sizeof(fail));        memset(f,0,sizeof(f));        mp.clear();        scanf("%d",&K);        for (int i=1;i<=K;i++)            scanf("%s",&s[i]);        scanf("%d",&m);        for (int i=0;i<m;i++)            //m个字符,重新编号         {            char opt[2];            scanf("%s",&opt);            scanf("%lf",&P[i]);            mp[opt[0]]=i;        }        for (int i=1;i<=K;i++)            insert(s[i]);        make();        scanf("%d",&n);        printf("Case #%d: %0.6lf\n",cas,doit(0,n));    }    return 0;}