zoukankan      html  css  js  c++  java
  • 题解 洛谷 P4569 【[BJWC2011]禁忌】

    考虑用(AC)自动机来解决本题这样的多字符串匹配问题。

    要最大化魔法分割后得到的禁忌串数目,最优情况肯定为在一个串中每个禁忌串的右端点进行分割。对应到(AC)自动机上,就是匹配到一个禁忌串后,就直接转移到根节点。

    若用朴素的(DP)解决,发现题目中的(len)过大,于是用矩阵快速幂优化。

    先构造初始矩阵,(a_{i,j})的值表示当串长为(1)时从状态(i)转移到状态(j)的概率,对这样的一个矩阵进行(len)次幂后,所得的含义即为串长为(len)时所对应的概率。

    同时新增一个状态(t)来统计期望,若转移过程中,转移到了一个合法的状态,即匹配上了一个禁忌串,那么就可以把当前概率统计到状态(t)上了,最后直接查询根到状态(t)即可。

    构造矩阵时,分情况讨论。设(P=frac{1}{alphabet}),若一个状态(x)可转移到状态(y),若状态(y)不是禁忌串的终止状态,则(a_{x,y})加上(P),否则让(a_{x,root})加上(P)(a_{x,t})加上(P)

    具体实现细节看代码吧。

    (code:)

    #include<bits/stdc++.h>
    #define maxn 110
    using namespace std;
    typedef long double ld;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,l,alph,tot,root;
    ld P;
    int trie[maxn][30],fail[maxn];
    bool end[maxn];
    char s[maxn];
    struct matrix
    {
        ld a[maxn][maxn];
    }m,e;
    matrix operator *(const matrix &x,const matrix &y)
    {
        matrix z;
        memset(z.a,0,sizeof(z.a));
        for(int k=root;k<=tot+1;++k)
            for(int i=root;i<=tot+1;++i)
                for(int j=root;j<=tot+1;++j)
                    z.a[i][j]+=x.a[i][k]*y.a[k][j];
        return z;
    }
    matrix qp(matrix x,int y)
    {
        matrix t=e;
        while(y)
        {
            if(y&1) t=t*x; 
            x=x*x;
            y>>=1;
        }
        return t;
    }
    void insert()
    {
        int len=strlen(s+1),p=root;
        for(int i=1;i<=len;++i)
        {
            int ch=s[i]-'a';
            if(!trie[p][ch]) trie[p][ch]=++tot;
            p=trie[p][ch];
        }
        end[p]=true;
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<alph;++i)
            if(trie[root][i])
                q.push(trie[root][i]);
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=0;i<alph;++i)
            {
                int y=trie[x][i];
                if(y)
                {
                    fail[y]=trie[fail[x]][i];
                    end[y]|=end[fail[y]],q.push(y);
                }
                else trie[x][i]=trie[fail[x]][i];
            }
        }
        e.a[tot+1][tot+1]=m.a[tot+1][tot+1]=1;
        for(int x=root;x<=tot;++x)
        {
            e.a[x][x]=1;
            for(int ch=0;ch<alph;++ch)
            {
                int y=trie[x][ch];
                if(end[y]) m.a[x][tot+1]+=P,m.a[x][root]+=P;
                else m.a[x][y]+=P;
            }
        }
    }
    int main()
    {
        read(n),read(l),read(alph),P=(ld)1.0/(ld)alph;
        for(int i=1;i<=n;++i)
            scanf("%s",s+1),insert();
        build(),m=qp(m,l);
        printf("%Lf",m.a[root][tot+1]);
        return 0;
    }
    
  • 相关阅读:
    floating IP 原理分析
    创建 floating IP
    Why Namespace?
    虚拟 ​router 原理分析- 每天5分钟玩转 OpenStack(101)
    链接脚本使用一例2---将二进制文件 如图片、MP3音乐、词典一类的东西作为目标文件中的一个段
    linux-2.6.26内核中ARM中断实现详解(转)
    有关Cache –(1) linux list之中的Prefetc
    Linux 内核中的 GCC 特性
    对entry-common.S和call.S的部分理解1
    kernel&uboot学习笔记
  • 原文地址:https://www.cnblogs.com/lhm-/p/12570407.html
Copyright © 2011-2022 走看看