题解 洛谷 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;
}
posted @ 2020-03-25 21:18  lhm_liu  阅读(333)  评论(0编辑  收藏  举报