UVA 11468 Substring AC自动机 概率DP

题意

给出一些字符和各自对应的选择概率,随机选择L次后得到一个长度为L的随机字符串S(每次独立随机)。给出K个模板串,计算S不包含任何一个串的概率(即任何一个模板串都不是S的连续子串)。

题解

对K个模板串建立AC自动机。
随机选择一个长度为L的随机字符串S就相当于在AC自动机上走L步。
设dp[u][L]表示当前在u结点上还需走L步且不包含模板串的概率。
根据全概率公式:
dp[u][L]=pro[vi]*dp[vi][L-1]+pro[vj]*dp[vj][L-1]+…+dp[vk][L-1].
需要根据建立AC自动机的过程确定在u结点有哪些子结点是可以往下走的。
设ed[v]=0表示可以往v结点走。
首先每个模板串的路径字符串的尾结点是不能走的,然后v的后缀结点是不能走的结点的话,那么v结点也是不能走的。即ed[v]|=ed[fail[v]].

AC代码

//320ms
#include <bits/stdc++.h>
using namespace std;

const int maxn=22;
int getid(char c)
{
    if(c<='z' && c>='a') return c-'a';
    if(c<='Z' && c>='A') return c-'A'+26;
    if(c<='9' && c>='0') return c-'0'+52;
}
double pro[62];
struct trie
{
    int ch[maxn*20][62],ed[maxn*20],sz,fail[maxn*20];
    double dp[maxn*20][110];
    bool vis[maxn*20][110];
    void init()
    {
        memset(ch[0],0,sizeof(ch[0]));
        memset(vis,false,sizeof(vis));
        memset(dp,0,sizeof(dp));
        sz=0;
    }
    void insert(char *s)
    {
        int len=strlen(s),p=0;
        for(int i=0;i<len;i++)
        {
            int c=getid(s[i]);
            if(!ch[p][c])
            {
                ch[p][c]=++sz;
                memset(ch[sz],0,sizeof(ch[sz]));
                ed[sz]=0;
            }
            p=ch[p][c];
        }
        ed[p]=1;
    }
    void build()
    {
        queue<int> que;
        for(int i=0;i<62;i++)
        {
            int &c=ch[0][i];
            if(c) fail[c]=0,que.push(c);
            else c=0;
        }
        while(!que.empty())
        {
            int now=que.front();que.pop();
            int nxt=fail[now];
            for(int i=0;i<62;i++)
            {
                int &c=ch[now][i];
                if(c) fail[c]=ch[nxt][i],que.push(c),ed[c]|=ed[fail[c]];
                else c=ch[nxt][i];
            }
        }
    }
    double dps(int u,int L)
    {
        if(!L) return 1.0;
        if(vis[u][L]) return dp[u][L];
        vis[u][L]=true;
        double &ans=dp[u][L];
        ans=0.0;
        for(int i=0;i<62;i++)
        {
            int v=ch[u][i];
            if(ed[v]) continue;
            ans+=pro[i]*dps(v,L-1);
        }
        return ans;
    }
}ac;

int main()
{
    int T,kase=0;
    scanf("%d",&T);
    while(T--)
    {
        int k;
        scanf("%d",&k);
        ac.init();
        while(k--)
        {
            char s[22];
            scanf("%s",s);
            ac.insert(s);
        }
        ac.build();
        int n;
        scanf("%d",&n);
        for(int i=0;i<62;i++) pro[i]=0.0;
        while(n--)
        {
            char s[2];
            scanf("%s",s);
            int c=getid(s[0]);
            scanf("%lf",&pro[c]);
        }
        int L;
        scanf("%d",&L);
        double ans=ac.dps(0,L);
        printf("Case #%d: %.6lf\n",++kase,ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值