BZOJ 2553: [BeiJing2011]禁忌

现在越写越觉得AC自动机之类的好简单233,明明之前觉得超难的说

首先我们容易想到一个关于划分的贪心,对于所有要划分的禁忌的字符串,划分点必然是它们的右端点

那么也就意味着如果我们在AC自动机上走,那么每遇到一个字符串的结尾就可以把贡献\(+1\)

所以我们容易想出一个DP,令\(f_{i,j}\)表示前\(i\)个字符,走到自动机上的\(j\)点的概率和是多少,由于每次的贡献是\(1\),因此这个总概率就是期望

转移的话根据下一个点的情况讨论,如果不是终止节点就直接转移,否则转移到根节点,并且累加上当前的概率

这样复杂度太高,但我们一眼就能看出每次转移其实是相通的,那么我们用矩乘优化

这里的矩阵构造应该是非常显然的吧,由于我们要构造的矩阵是一步转移的概率,因此只用考虑每个点和它的子节点即可

注意最后的概率是一个和的形式,因此要多开一维来记录和,每次转移到根节点的时候同时也转移到这一维去

写一发交一下WA了,随手改个long double上去就过了233

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=80;
struct Matrix
{
    int n,m; long double mat[N][N];
    inline Matrix(CI N=0,CI M=0)
    {
        n=N; m=M; memset(mat,0,sizeof(mat));
    }
    inline long double* operator [] (CI x) { return mat[x]; }
    friend inline Matrix operator * (Matrix A,Matrix B)
    {
        Matrix C(A.n,B.m); for (RI i=0,j,k;i<C.n;++i)
        for (j=0;j<C.m;++j) for (k=0;k<A.m;++k)
        C[i][j]+=A[i][k]*B[k][j]; return  C;
    }
    friend inline Matrix operator ^ (Matrix A,int p)
    {
        Matrix T(A.n,A.n); for (RI i=0;i<T.n;++i) T[i][i]=1;
        for (;p;p>>=1,A=A*A) if (p&1) T=A*T; return T;
    }
}a; int n,len,m; char s[N];
class AC_Automation
{
    private:
        struct ac_node
        {
            int ch[26],fail;
        }node[N]; int tot,q[N]; bool end[N];
    public:
        #define next(x,y) node[x].ch[y]
        #define fail(x) node[x].fail
        inline void insert(char *s)
        {
            int len=strlen(s+1),now=0; for (RI i=1;i<=len;++i)
            {
                if (!next(now,s[i]-'a')) next(now,s[i]-'a')=++tot;
                now=next(now,s[i]-'a');
            }
            end[now]=1;
        }
        inline void get_fail(void)
        {
            RI H=0,T=0,i; for (i=0;i<m;++i) if (next(0,i)) q[++T]=next(0,i);
            while (H<T)
            {
                int now=q[++H],to; for (i=0;i<m;++i)
                if (!(to=next(now,i))) next(now,i)=next(fail(now),i);
                else end[to]|=end[fail(q[++T]=to)=next(fail(now),i)];
            }
        }
        inline long double solve(void)
        {
            long double p=1.0/m; for (RI i=0,j;i<=tot;++i) for (j=0;j<m;++j)
            if (end[next(i,j)]) a[i][tot+1]+=p,a[i][0]+=p; else a[i][next(i,j)]+=p;
            a[tot+1][tot+1]=1; a.n=a.m=tot+2; a=a^len; return a[0][tot+1];
        }
        #undef next
        #undef fail
}AC;
int main()
{
    RI i; for (scanf("%d%d%d",&n,&len,&m),i=1;i<=n;++i)
    scanf("%s",s+1),AC.insert(s); AC.get_fail();
    return printf("%.7Lf",AC.solve()),0;
}
posted @ 2020-02-02 21:21  空気力学の詩  阅读(125)  评论(0编辑  收藏  举报