zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    130517Dev GridControl建立多行复杂表头(Banded View)时,统计列与对应列无法对齐的解决办法
    C&C++标准库
    Linux操作系统下的多线程编程详细解析
    Ubuntu12.04用户以root身份登录
    ubuntu永久修改主机名
    linux信号 linux signal
    淘宝api 登录验证
    淘宝开店 防骗 易赛加款诈骗|冲q币恶意差评
    面试..
    test
  • 原文地址:https://www.cnblogs.com/cjjsb/p/12253783.html
Copyright © 2011-2022 走看看