P4569 [BJWC2011]禁忌
题意简述:给出大小为 \(n\) 的字典 \(s\)。设函数 \(g(t)\) 表示 \(t\) 最多能被分割成的单词个数。等概率随机生成长度为 \(len\) 的字符串 \(T\),求 \(E(g(t))\)。
hot tea. 比较像 P3193 [HNOI2008]GT考试。
首先对 \(s_i\) 建出 ACAM,然后在上面 DP。设 \(f_{i,j}\) 表示关于所有 \(T\)(\(|T|=i\) 且 \(T\) 在 ACAM 上能跑到状态 \(j\))的一个东西。那么究竟是表示什么呢?
记 \(P=\dfrac{f_{i,j}}{a}\);\(p\) 为 \(j\) 的所有子节点,即 \(p\in son_j\)。
错误思路:如果 \(f_{i,j}\) 单纯表示字符串 \(T\) 的 “概率”(即 \(\dfrac{num(T)}{a^i}\)),那么转移与统计答案就是:如果 \(p\) 是终止节点,将 \(ans\) 加上 \(P\);同时将所有 \(f_{i+1,p}\) 加上 \(P\)。不错,如果 \(g(T)\) 表示的是所有单词 \(s_i\) 在 \(T\) 中出现次数之和,那么这样是没错的,可惜不是,因为会有重复计算,即若字典 \(s=\{\texttt{ab,bc}\}\),那么 \(T=\{\texttt{abc}\}\) 会算入两次贡献(样例中也有提到这种情况)。
正确思路:注意到这个 "最多" 有点麻烦,不过还是有处理的办法:如果 \(p\) 是一个终止节点,那么在下传概率的时候,将 \(f_{i+1,0}\)(而不是 \(f_{i+1,p}\))加上 \(P\)。如果成功匹配一个单词,就必须从头开始匹配。
注意到 \(\sum |s_i|\) 很小,只有 \(75\)。这也意味着 \(j\) 的范围只有 \(75\)。因此,用矩阵快速幂加速 DP 即可,别忘了在矩阵中留个位置记录 \(ans\)。
总时间复杂度 \(\mathcal{O}((\sum |s_i|)^3\log len)\)。
/*
Powered by C++11.
Author : Alex_Wei.
*/
#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(3)
//#define int long long
using ld = long double;
const int N=77;
const int S=26;
int sz,al,son[N][S],fa[N],ed[N];
void ins(string s){
int p=0;
for(char it:s){
if(!son[p][it-'a'])son[p][it-'a']=++sz;
p=son[p][it-'a'];
} ed[p]=1;
}
void build(){
queue <int> q;
for(int i=0;i<al;i++)if(son[0][i])q.push(son[0][i]);
while(!q.empty()){
int t=q.front(); q.pop();
for(int i=0;i<al;i++)
if(son[t][i])q.push(son[t][i]),fa[son[t][i]]=son[fa[t]][i];
else son[t][i]=son[fa[t]][i];
ed[t]|=ed[fa[t]];
}
}
struct mat{
ld a[N][N];
friend mat operator * (mat x,mat y){
mat z; mem(z.a,0);
for(int i=0;i<=sz;i++)
for(int j=0;j<=sz;j++)
for(int k=0;k<=sz;k++)
z.a[i][j]+=x.a[i][k]*y.a[k][j];
return z;
}
}base,ans;
int n,len;
int main(){
cin>>n>>len>>al;
for(int i=0;i<n;i++){
string s;
cin>>s,ins(s);
} build();
for(int i=0;i<=sz;i++)
for(int j=0;j<al;j++){
int p=son[i][j];
if(ed[p])base.a[i][0]+=1.0/al,base.a[i][sz+1]+=1.0/al;
else base.a[i][p]+=1.0/al;
}
sz++,ans.a[0][0]=base.a[sz][sz]=1;
while(len){
if(len&1)ans=ans*base;
base=base*base,len>>=1;
} printf("%.10Lf\n",ans.a[0][sz]);
return 0;
}
P4569 [BJWC2011]禁忌的更多相关文章
- 洛谷 P4569 - [BJWC2011]禁忌(AC 自动机+矩阵乘法)
题面传送门 又好久没做过 AC 自动机的题了,做道练练手罢( 首先考虑对于某个固定的字符串怎样求出它的伤害,我们考虑贪心,每碰到出现一个模式串就将其划分为一段,最终该字符串的代价就是划分的次数.具体来 ...
- 题解 洛谷 P4569 【[BJWC2011]禁忌】
考虑用\(AC\)自动机来解决本题这样的多字符串匹配问题. 要最大化魔法分割后得到的禁忌串数目,最优情况肯定为在一个串中每个禁忌串的右端点进行分割.对应到\(AC\)自动机上,就是匹配到一个禁忌串后, ...
- BZOJ2553 [BJWC2011]禁忌
传送门 Description 给你前alphabet个小写字母组成的字符集, 以及n个单词, 定义一个串s的禁忌值为 \(\sum_{i } [s[i] == Taboo[i]]\) , Tab ...
- BJWC2011 禁忌
题目链接 题解 多模式匹配首先建 AC 自动机,看到 \(len \le 10^9\) 想到矩阵乘法优化. 朴素 DP 关于分割的最大值,可以贪心,只要走到一个能匹配串的点立刻返回根继续匹配就行,一定 ...
- [BJWC2011]禁忌 AC 自动机 概率与期望
#include<cstdio> #include<algorithm> #include<cstring> #include<string> #inc ...
- DP 优化方法大杂烩 & 做题记录 I.
标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...
- ACAM 题乱做
之前做了不少 ACAM,不过没怎么整理起来,还是有点可惜的. 打 * 的是推荐一做的题目. I. *CF1437G Death DBMS 见 我的题解. II. *CF1202E You Are Gi ...
- 「刷题笔记」AC自动机
自动AC机 Keywords Research 板子题,同luoguP3808,不过是多测. 然后多测不清空,\(MLE\)两行泪. 板子放一下 #include<bits/stdc++.h&g ...
- [No000052]大蒜怎么吃最美容?吃大蒜的功效及禁忌
大蒜是最常见的香辛调味料,它被称为天然抗生素,富含大蒜素等多种营养物质和抗氧化剂,具有多种美肤美容作用. 大蒜的5种美容功效 1.除皱.大蒜里的某些成分,有类似维生素E与维生素C的抗氧化.防衰老特性, ...
随机推荐
- 4.19——数组双指针——26. 删除有序数组中的重复项 & 27. 删除有序数组中的重复项II & 80. 删除有序数组中的重复项 II
第一次做到数组双指针的题目是80: 因为python的List是可以用以下代码来删除元素的: del List[index] 所以当时的我直接用了暴力删除第三个重复元素的做法,大概代码如下: n = ...
- Jupyter Notebook配置多个kernel
Jupyter Notebook配置多个kernel 前言: 在anaconda下配置了多个环境,而Jupiter Notebook只是安装在base环境下,为了能在Jupiter Notebook中 ...
- SpringBoot整合Prometheus
SpringBoot整合Prometheus 一.需求 二.实现步骤 1.引入jar包 2.application.prometheus文件配置 3.查看指标数据 4.接入到 prometheus 中 ...
- 2021.8.18 NKOJ周赛总结
两个字总结:安详 T1: NKOJ-6179 NP问题 问题描述: p6pou在平面上画了n个点,并提出了一个问题,称为N-Points问题,简称NP问题. p6pou首先在建立的平面直角坐标系,并标 ...
- Celery Task(定时任务)及参数
celery beat 是一个调度器:它以常规的时间间隔开启任务,任务将会在集群中的可用节点上运行. 默认情况下,入口项是从 beat_schedule 设置中获取,但是自定义的存储也可以使用,例如在 ...
- Swift-方法调度-类的普通方法底层探究
1. 类的普通方法调度 写一个结构体和一个类,对比看看方法调用的方式: // 结构体 struct PersonStruct { func changClassName() {} } let s = ...
- 整数转化 牛客网 程序员面试金典 C++ Python
整数转化 牛客网 程序员面试金典 C++ Python 题目描述 编写一个函数,确定需要改变几个位,才能将整数A转变成整数B. 给定两个整数int A,int B.请返回需要改变的数位个数. 测试样例 ...
- Vue面试题2
Class与Style绑定工作有用过吗: 有,后台管理系统菜单.主题色切换 .tab选项卡等..... 计算属性和侦听器区别.使用场景: 计算属性有缓存.并且是响应式依赖缓存,调用不加小括号 利用vu ...
- js和jq文档操作
JS文档操作 一.dom树结构 1.元素节点 2.文本节点 3.属性节点 不属于元素节点的子节点 4.文档节点(document) 二.处理元素节点 method 1.docu ...
- Matlab 中 arburg 函数的理解与实际使用方法
1. 理解 1.1 Matlab 帮助: a = arburg(x,p)返回与输入数组x的p阶模型相对应的归一化自回归(AR)参数. 如果x是一个向量,则输出数组a是一个行向量. 如果x是矩阵,则参数 ...