bzoj 2553: [BeiJing2011]禁忌 AC自动机+矩阵乘法+期望

题意

给出n个串,定义一个长度为len的字符串的权值为:所有拆分方案中,最大的完全等于这n个串中一个的部分的数量。求所有长度为len的字符串的权值期望。
n<=5,len<=10^9,每个字符串长度<=15。

分析

显然可以上AC自动机。
一开始的思路是设f[i,j]表示走了i步走到节点j的期望权值,然后建出状态转移矩阵后发现无法转移。搜了题解才发现可以设f[i,j]表示走了i步走到节点j的概率,然后多开一维计数器来计算期望。然后就可以直接转移了。
这告诉我们以后做期望题的时候可以考虑先求概率。
要开long double。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int N=80;

int n,m,len,ch[N][26],sz,val[N],fail[N];
char str[20];
queue<int> que;

struct Matrix
{
    long double a[N][N];

    void clear(int n)
    {
        for (int i=0;i<=n;i++)
            for (int j=0;j<=n;j++)
                a[i][j]=0;
    }

    void unit(int n)
    {
        clear(n);
        for (int i=0;i<=n;i++) a[i][i]=1;
    }
}a;

void ins()
{
    int len=strlen(str),now=0;
    for (int i=0;i<len;i++)
        if (!ch[now][str[i]-'a']) now=ch[now][str[i]-'a']=++sz;
        else now=ch[now][str[i]-'a'];
    val[now]=1;
}

void get_fail()
{
    for (int i=0;i<m;i++) if (ch[0][i]) que.push(ch[0][i]);
    while (!que.empty())
    {
        int u=que.front();que.pop();
        for (int i=0;i<m;i++)
            if (ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i],que.push(ch[u][i]);
            else ch[u][i]=ch[fail[u]][i];
    }
    long double w=(long double)1/m;sz++;
    for (int i=0;i<sz;i++)
    {
        if (val[i]) continue;
        for (int j=0;j<m;j++)
            if (!val[ch[i][j]]) a.a[i][ch[i][j]]+=w;
            else a.a[i][0]+=w,a.a[i][sz]+=w;
    }
    a.a[sz][sz]=1;
}

double ksm(double x,int y)
{
    double ans=1;
    while (y)
    {
        if (y&1) ans*=x;
        x*=x;y>>=1;
    }
    return ans;
}

void mul(Matrix &c,Matrix a,Matrix b)
{
    c.clear(sz);
    for (int i=0;i<=sz;i++)
        for (int k=0;k<=sz;k++)
            for (int j=0;j<=sz;j++)
                c.a[i][j]+=a.a[i][k]*b.a[k][j];
}

Matrix Matrix_ksm(Matrix x,int y)
{
    Matrix ans;ans.unit(sz);
    while (y)
    {
        if (y&1) mul(ans,ans,x);
        mul(x,x,x);y>>=1;
    }
    return ans;
}

int main()
{
    scanf("%d%d%d",&n,&len,&m);
    for (int i=1;i<=n;i++) scanf("%s",str),ins();
    get_fail();
    a=Matrix_ksm(a,len);
    printf("%.10lf",(double)a.a[0][sz]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值