BZOJ2553 Beijing2011禁忌(AC自动机+动态规划+矩阵快速幂+概率期望)

  考虑对一个串如何分割能取得最大值。那么这是一个经典的线段覆盖问题,显然每次取右端点尽量靠前的串。于是可以把串放在AC自动机上跑,找到一个合法串后就记录并跳到根。

  然后考虑dp。设f[i][j]表示前i位走到AC自动机上j节点的概率,枚举下个字符即可转移。同时记录此时期望伤害,找到合法串就统计入答案。

  并且注意到每次转移是相同的。矩阵快速幂优化即可。

  以及非常卡精度,需要全程long double。cout的保留小数位数误差是相当大的,必须用printf。并且转移到某个字符的概率即1/alphabet需要强制转换成long double,否则也会丢失精度。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 80
#define double long double
int n,m,S,trie[N][26],fail[N],val[N],q[N],cnt=0;
char c[20];
struct matrix
{
    int n;double a[N][N];
    matrix operator *(const matrix&b) const
    {
        matrix c;c.n=n;memset(c.a,0,sizeof(c.a));
        for (int i=0;i<n;i++)
            for (int j=0;j<N;j++)
                for (int k=0;k<N;k++)
                c.a[i][j]+=a[i][k]*b.a[k][j];
        return c;
    }
}f,a;
void ins(char *a)
{
    int n=strlen(a+1),t=0;
    for (int i=1;i<=n;i++)
    {
        if (!trie[t][a[i]-'a']) trie[t][a[i]-'a']=++cnt;
        t=trie[t][a[i]-'a'];
    }
    val[t]=1;
}
void build()
{
    int head=0,tail=0;for (int i=0;i<S;i++) if (trie[0][i]) q[++tail]=trie[0][i];
    do
    {
        int x=q[++head];
        for (int i=0;i<S;i++)
        if (trie[x][i]) q[++tail]=trie[x][i],fail[trie[x][i]]=trie[fail[x]][i],val[trie[x][i]]|=val[fail[trie[x][i]]];
        else trie[x][i]=trie[fail[x]][i];
    }while (head<tail);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2553.in","r",stdin);
    freopen("bzoj2553.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read(),S=read();
    for (int i=1;i<=n;i++) scanf("%s",c+1),ins(c);
    build();
    a.n=cnt+2;
    for (int i=0;i<=cnt;i++)
        for (int j=0;j<S;j++)
        if (val[trie[i][j]]) a.a[i][0]+=(double)1/S,a.a[i][cnt+1]+=(double)1/S;
        else a.a[i][trie[i][j]]+=(double)1/S;
    a.a[cnt+1][cnt+1]=1;
    f.n=1;f.a[0][0]=1;
    for (;m;a=a*a,m>>=1) if (m&1) f=f*a;
    printf("%.8Lf",f.a[0][cnt+1]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9581650.html