BZOJ2553 [BeiJing2011]禁忌 AC自动机 矩阵

原文链接http://www.cnblogs.com/zhouzhendong/p/8196279.html


题目传送门 - BZOJ2553


题意概括

  引用一下lych大佬的:

  在字母只有前alphabet时,给定N个串,求长度为len的串包含这些N个串的个数最大值的期望值。


题解

  我们首先发现总共的字符个数才就75个,那么闭着眼睛先建一个AC自动机,Trie图建好。反正代码不长。

  然后我们发现长度很大,显然就是要往矩阵快速幂那里考虑。

  我们可以用矩阵来快速计算到达AC自动机的每一个位置的概率。

  然后我们考虑原问。

  对于一个串,把它划分成包含最多的禁忌串数的方案怎么做?

  我们可以贪心的来,从头开始沿着字符串在AC自动机上面走,每一次到一个结束点就划分。这样一定是最优的。

  然后回到矩阵中。

  假如说我们考虑DP的话,那么每到一个结束点,都要统计一下当前概率对答案的贡献,就是概率*1

  那么我们可以把它记入答案中。

  同理,我们可以在矩阵中多开一列,用来维护答案。

 


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long double LD;
const int S=80;
struct Trie{
    int fail,e,Next[26];
    void clear(){
        fail=e=0;
        memset(Next,0,sizeof Next);
    }
}tree[S];
int n,len,alpha,cnt;
void build(char ch[]){
    int rt=1,t,len=strlen(ch);
    for (int i=0;i<len;i++){
        t=ch[i]-'a';
        if (!tree[rt].Next[t]){
            tree[++cnt].clear();
            tree[rt].Next[t]=cnt;
        }
        rt=tree[rt].Next[t];
    }
    tree[rt].e=1;
}
void build_AC(){
    int q[S],head=0,tail=0;
    int rt,son,k;
    q[++tail]=1,tree[0].fail=1;
    while (head<tail){
        int rt=q[++head];
        for (int i=0;i<alpha;i++){
            son=tree[rt].Next[i];
            if (!son){
                tree[rt].Next[i]=tree[tree[rt].fail].Next[i];
                continue;
            }
            k=tree[rt].fail;
            while (!tree[k].Next[i])
                k=tree[k].fail;
            tree[son].fail=tree[k].Next[i];
            tree[son].e|=tree[tree[k].Next[i]].e;
            q[++tail]=son;
        }
    }
}
int m;
struct Mat{
    LD v[S][S];
    Mat (){}
    Mat (int x){set(x);}
    void print(){
        for (int i=1;i<=m;i++,puts(""))
            for (int j=1;j<=m;j++)
                printf("%5.3Lf ",v[i][j]);
        puts("");
    }
    void set(int x){
        memset(v,0,sizeof v);
        if (x==1)
            for (int i=1;i<=m;i++)
                v[i][i]=1;
    }
    Mat operator * (Mat b){
        Mat ans(0);
        for (int i=1;i<=m;i++)
            for (int j=1;j<=m;j++)
                for (int k=1;k<=m;k++)
                    ans.v[i][j]+=v[i][k]*b.v[k][j];
        return ans;
    }
    Mat operator ^ (int y){
        Mat ans(1),x=*this;
        while (y){
            if (y&1)
                ans=ans*x;
            x=x*x;
            y>>=1;
        }
        return ans;
    }
}M(0);
char s[S];
int main(){
    scanf("%d%d%d",&n,&len,&alpha);
    cnt=1;
    tree[0].clear();
    tree[1].clear();
    for (int i=0;i<alpha;i++)
        tree[0].Next[i]=1;
    for (int i=1;i<=n;i++){
        scanf("%s",s);
        build(s);
    }
    build_AC();
    m=cnt+1;
    LD addv=1.0/(1.0*alpha);
    for (int i=1;i<=cnt;i++)
        for (int j=0;j<alpha;j++)
            if (tree[tree[i].Next[j]].e)
                M.v[i][1]+=addv,M.v[i][m]+=addv;
            else
                M.v[i][tree[i].Next[j]]+=addv;
    M.v[m][m]=1;
    Mat Mans=M^len;
    printf("%.10lf",(double)Mans.v[1][m]);
    return 0;
}

  

 

posted @   zzd233  阅读(253)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术

点击右上角即可分享
微信分享提示