考虑用\(AC\)自动机来解决本题这样的多字符串匹配问题。

要最大化魔法分割后得到的禁忌串数目,最优情况肯定为在一个串中每个禁忌串的右端点进行分割。对应到\(AC\)自动机上,就是匹配到一个禁忌串后,就直接转移到根节点。

若用朴素的\(DP\)解决,发现题目中的\(len\)过大,于是用矩阵快速幂优化。

先构造初始矩阵,\(a_{i,j}\)的值表示当串长为\(1\)时从状态\(i\)转移到状态\(j\)的概率,对这样的一个矩阵进行\(len\)次幂后,所得的含义即为串长为\(len\)时所对应的概率。

同时新增一个状态\(t\)来统计期望,若转移过程中,转移到了一个合法的状态,即匹配上了一个禁忌串,那么就可以把当前概率统计到状态\(t\)上了,最后直接查询根到状态\(t\)即可。

构造矩阵时,分情况讨论。设\(P=\frac{1}{alphabet}\),若一个状态\(x\)可转移到状态\(y\),若状态\(y\)不是禁忌串的终止状态,则\(a_{x,y}\)加上\(P\),否则让\(a_{x,root}\)加上\(P\)和\(a_{x,t}\)加上\(P\)。

具体实现细节看代码吧。

\(code:\)

#include<bits/stdc++.h>
#define maxn 110
using namespace std;
typedef long double ld;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,l,alph,tot,root;
ld P;
int trie[maxn][30],fail[maxn];
bool end[maxn];
char s[maxn];
struct matrix
{
ld a[maxn][maxn];
}m,e;
matrix operator *(const matrix &x,const matrix &y)
{
matrix z;
memset(z.a,0,sizeof(z.a));
for(int k=root;k<=tot+1;++k)
for(int i=root;i<=tot+1;++i)
for(int j=root;j<=tot+1;++j)
z.a[i][j]+=x.a[i][k]*y.a[k][j];
return z;
}
matrix qp(matrix x,int y)
{
matrix t=e;
while(y)
{
if(y&1) t=t*x;
x=x*x;
y>>=1;
}
return t;
}
void insert()
{
int len=strlen(s+1),p=root;
for(int i=1;i<=len;++i)
{
int ch=s[i]-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;
p=trie[p][ch];
}
end[p]=true;
}
void build()
{
queue<int> q;
for(int i=0;i<alph;++i)
if(trie[root][i])
q.push(trie[root][i]);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<alph;++i)
{
int y=trie[x][i];
if(y)
{
fail[y]=trie[fail[x]][i];
end[y]|=end[fail[y]],q.push(y);
}
else trie[x][i]=trie[fail[x]][i];
}
}
e.a[tot+1][tot+1]=m.a[tot+1][tot+1]=1;
for(int x=root;x<=tot;++x)
{
e.a[x][x]=1;
for(int ch=0;ch<alph;++ch)
{
int y=trie[x][ch];
if(end[y]) m.a[x][tot+1]+=P,m.a[x][root]+=P;
else m.a[x][y]+=P;
}
}
}
int main()
{
read(n),read(l),read(alph),P=(ld)1.0/(ld)alph;
for(int i=1;i<=n;++i)
scanf("%s",s+1),insert();
build(),m=qp(m,l);
printf("%Lf",m.a[root][tot+1]);
return 0;
}

题解 洛谷 P4569 【[BJWC2011]禁忌】的更多相关文章

  1. 洛谷 P4569 - [BJWC2011]禁忌(AC 自动机+矩阵乘法)

    题面传送门 又好久没做过 AC 自动机的题了,做道练练手罢( 首先考虑对于某个固定的字符串怎样求出它的伤害,我们考虑贪心,每碰到出现一个模式串就将其划分为一段,最终该字符串的代价就是划分的次数.具体来 ...

  2. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  3. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

  4. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  5. 题解-洛谷P4229 某位歌姬的故事

    题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...

  6. 题解-洛谷P4724 【模板】三维凸包

    洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...

  7. 题解-洛谷P4859 已经没有什么好害怕的了

    洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...

  8. 题解-洛谷P5217 贫穷

    洛谷P5217 贫穷 给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作: \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\). \(\te ...

  9. 题解 洛谷 P2010 【回文日期】

    By:Soroak 洛谷博客 知识点:模拟+暴力枚举 思路:题目中有提到闰年然后很多人就认为,闰年是需要判断的其实,含有2月29号的回文串,前四位是一个闰年那么我们就可以直接进行暴力枚举 一些小细节: ...

随机推荐

  1. 处理TortoiseGit一直弹出密码框的方法 -输入git@XXXX.com的密码

    问题 :在push和pull的时候,一直都弹出这个框 1.开始处搜索TortoiseGit文件夹,找到其中的“PuTTYgen”文件,如下显示 2.运行之后在弹出的窗口中点击下方的“Generate” ...

  2. Unity常见的三种数据本地持久化方案

    做游戏的时候常常会有数据配置或者存读档的需求,本文整理了常用的几种解决方案,分别是Unity自带的PlayerPrefs类,XML文件和Json文件. 一. PlayerPrefs 这是Unity自带 ...

  3. python实用笔记——IO编程

    打开文件 f = open('/Users/michael/test.txt', 'r') 再读取 >>> f.read() 'Hello, world!' 最后关闭 >> ...

  4. vue 生命周期钩子 路由钩子 动画钩子 执行顺序

    进入首页的钩子们 1 路由钩子 路由跳转前beforeEach 2 路由钩子 home组件内部:守卫执行前beforeRouteEnter 3.路由钩子 路由跳转后afterEach 4 生命周期 h ...

  5. socket 建立网络连接,client && server

    client代码: package socket; import java.io.IOException; import java.net.Socket; /** * 客户端_聊天室 * * @aut ...

  6. STL初步学习(map)

    3.map map作为一个映射,有两个参数,第一个参数作为关键值,第二个参数为对应的值,关键值是唯一的 在平时使用的数组中,也有点类似于映射的方法,例如a[10]=1,但其实我们的关键值和对应的值只能 ...

  7. .NET 开源项目 StreamJsonRpc 介绍

    StreamJsonRpc 是一个实现了 JSON-RPC 通信协议的开源 .NET 库,在介绍 StreamJsonRpc 之前,我们先来了解一下 JSON-RPC. JSON-RPC 介绍 JSO ...

  8. Cache写策略(Cache一致性问题与骚操作)

    写命中 写直达(Write Through) 信息会被同时写到cache的块和主存中.这样做虽然比较慢,但缺少代价小,不需要把整个块都写回主存.也不会发生一致性问题. 对于写直达,多出来%10向主存写 ...

  9. 猿灯塔-Phaser 使用介绍

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 本文将介绍 java.util.concurrent.Phaser,一个常常被大家忽略的并发工具.它和 CyclicBarrier 以及 Co ...

  10. 自适应高度输入框(contenteditable/textarea)

      一.用div模拟textarea div模拟输入域可以根据输入内容自动伸缩,而input和textarea输入内容较多时,高度固定,内容会显示不全. 1.坑1(IOS端无法输入) 在取消全局默认样 ...