Uva11468 AC自动机+概率dp

这题把AC自动机和dp结合了下,题意就是给你一些模式串,给你选择每个字符的概率,让你选择L个字符,求不出现模式串的概率。

先把模式串都插入AC自动机,用一个match数组记录该节点是否是单词节点,特别注意下这里单词节点可能是从0节点开始,也肯能由后缀构成单词节点,所以得加一句match[i]|=match[f[i]],即失配指针指向的点如果是单词节点也可以构成改点。

这题的dp思想比较简单:

dp[i][j]表示还有i个字母要选,当前在j这个节点。

dp[i][j]=sum{p[c]*dp[i-1][ch[j][c]] } 搜索当前节点的所有不是单词节点的子节点,乘以其概率求和。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 10005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
#define M 62
#define N 444
using namespace std;
int getnum(char ch)
{
    if('0'<=ch&&ch<='9') return ch-'0';
    else if('A'<=ch&&ch<='Z') return ch-'A'+10;
    else
    return ch-'a'+36;
}
struct AC
{
    int f[N],ch[N][M];
    bool match[N];
    int tot;
    void init()
    {
        tot=0;
        f[0]=match[0]=0;
        memset(ch[0],0,sizeof(ch[0]));
    }
    void insert(char *s)
    {
        int l=strlen(s);
        int p=0;
        for(int i=0;i<l;i++)
        {
            int c=getnum(s[i]);
            if(!ch[p][c])
            {
                ch[p][c]=++tot;
                f[tot]=0;match[tot]=0;memset(ch[tot],0,sizeof(ch[tot]));
            }
            p=ch[p][c];
        }
        match[p]=1;
    }
    void getfail()
    {
        queue<int> q;
        for(int i=0;i<M;i++)
        {
            int u=ch[0][i];
            if(u)
            {
                f[u]=0;           
                q.push(u);
            }
        }
        while(!q.empty())
        {
            int r=q.front();q.pop();
            match[r]|=match[f[r]];//如果失配指针指向的位置是匹配串,那么这里也应该是
            for(int i=0;i<M;i++)
            {
                int u=ch[r][i];
                if(!u)
                {
                    ch[r][i]=ch[f[r]][i];continue;
                }
                q.push(u);
                f[u]=ch[f[r]][i];
            }
        }
    }
}ac;
char s[22][22];
double p[100];
double dp[105][N];
bool vis[105][N];
double DP(int i,int j)
{
    if(!i) return 1.0;
    if(vis[i][j]) return dp[i][j];
    vis[i][j]=1;
    dp[i][j]=0;
    for(int c=0;c<62;c++)
    {
        if(!ac.match[ac.ch[j][c]]) dp[i][j]+=p[c]*DP(i-1,ac.ch[j][c]);
    }
    return dp[i][j];
}
void init()
{
    memset(p,0,sizeof(p));
    memset(vis,0,sizeof(vis));
}
int main()
{
    int t,tt=1;
    scanf("%d",&t);
    while(t--)
    {
        ac.init();
        int k,n;
        scanf("%d",&k);
        for(int i=1;i<=k;i++)
        {
            scanf("%s",s[i]);
            ac.insert(s[i]);
        }
        ac.getfail();
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            char ch[5];
            double tp;
            scanf("%s",ch); scanf("%lf",&tp);
            p[getnum(ch[0])]=tp;
        }
        int l;
        scanf("%d",&l);
        printf("Case #%d: %.6lf\n",tt++,DP(l,0));
    }
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值