【BZOJ】4861: [Beijing2017]魔法咒语 AC自动机+DP+矩阵快速幂
【题意】给定n个原串和m个禁忌串,要求用原串集合能拼出的不含禁忌串且长度为L的串的数量。(60%)n,m<=50,L<=100。(40%)原串长度为1或2,L<=10^18。
【算法】AC自动机+DP+矩阵快速幂
【题解】其实题意的数据范围不太清晰,反正开200个点就足够了。
因为要匹配禁忌串,所以对禁忌串集合建立AC自动机,标记禁忌串结尾节点,以及下传到所有能fail到的点(这些点访问到都相当于匹配了禁忌串)。
令f[i][j]表示匹配到节点i,长度为j的串的数量,先预处理a[i][j]表示节点 i 匹配第 j 个原串到达的节点编号,那么就有:
f [ a[i][j] ] [ L+size[j] ] += f [ i ] [ L ]
以上就是60%数据的做法,对于40%的数据使用矩阵快速幂。
假设原串长度均为1,那么DP的转移如下:
$$f[i][L]=\sum_{j}f[j][L-1]\ \ ,\ \ j \rightarrow i$$
这很容易用一个长度为第一维大小(AC自动机节点数)的矩阵维护转移,第L个列向量就是f[i][L]。
如果原串长度有2,那么再记录L-1即可。
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=,MOD=1e9+;
int n,m,a[maxn][],ch[maxn][],val[maxn],size[maxn],sz=,fail[maxn];
ll L;
char s[][maxn],S[maxn];
queue<int>Q;
void insert(char *s){
int n=strlen(s),u=;
for(int i=;i<n;i++){
int c=s[i]-'a';
if(!ch[u][c])ch[u][c]=++sz;
u=ch[u][c];
}
val[u]++;
}
void AC_build(){
for(int c=;c<;c++)if(ch[][c])Q.push(ch[][c]);
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int c=;c<;c++)if(ch[u][c]){
fail[ch[u][c]]=ch[fail[u]][c];
Q.push(ch[u][c]);
val[ch[u][c]]|=val[fail[ch[u][c]]];//
}
else ch[u][c]=ch[fail[u]][c];
}
}
int M(int x){return x>=MOD?x-MOD:x;}
namespace Task1{
int f[maxn][];
void solve(){
f[][]=;
for(int l=;l<L;l++){//
for(int i=;i<=sz;i++)if(f[i][l]){
for(int j=;j<=n;j++)if(~a[i][j]&&l+size[j]<=L){
f[a[i][j]][l+size[j]]=M(f[a[i][j]][l+size[j]]+f[i][l]);
}
}
}
int ans=;
for(int i=;i<=sz;i++)if(f[i][L]&&!val[i])ans=M(ans+f[i][L]);
printf("%d",ans);
}
}
namespace Task2{
const int maxn=;
int N,A[maxn*][maxn*],ANS[maxn*][maxn*],c[maxn*][maxn*];
void mul(int a[maxn*][maxn*],int b[maxn*][maxn*]){
for(int i=;i<=N;i++){
for(int j=;j<=N;j++){
c[i][j]=;
for(int k=;k<=N;k++)c[i][j]=M(c[i][j]+1ll*a[i][k]*b[k][j]%MOD);
}
}
for(int i=;i<=N;i++)for(int j=;j<=N;j++)b[i][j]=c[i][j];
}
void solve(){
N=sz*+;
for(int i=;i<=sz;i++){
for(int j=;j<=n;j++)if(~a[i][j]){
if(size[j]==)A[a[i][j]*][i*]++;
else A[a[i][j]*][i*+]++;
}
A[i*+][i*]=;
}
ANS[][]=;
while(L){
if(L&)mul(A,ANS);
mul(A,A);
L>>=;
}
int ans=;
for(int i=;i<=sz;i++)if(!val[i])ans=M(ans+ANS[i*][]);
printf("%d",ans);
}
}
int main(){
scanf("%d%d%lld",&n,&m,&L);
for(int i=;i<=n;i++)scanf("%s",s[i]);
for(int i=;i<=m;i++){
scanf("%s",S);
insert(S);
}
AC_build();
memset(a,-,sizeof(a));
for(int k=;k<=n;k++){
size[k]=strlen(s[k]);
for(int i=;i<=sz;i++){
int u=i;
for(int j=;j<size[k];j++)if(!val[u])u=ch[u][s[k][j]-'a'];else break;
if(!val[u])a[i][k]=u;
}
}
if(L<=)Task1::solve();else
Task2::solve();
return ;
}
【BZOJ】4861: [Beijing2017]魔法咒语 AC自动机+DP+矩阵快速幂的更多相关文章
- Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂)
Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂) 题目链接 题解: 多串匹配问题,很容易想到是AC自动机 先构建忌讳词语的AC自动机,构建时顺便记录一下这个点以及它的所有后 ...
- 【BZOJ】2553: [BeiJing2011]禁忌 AC自动机+期望+矩阵快速幂
[题意]给定n个禁忌字符串和字符集大小alphabet,保证所有字符在集合内.一个字符串的禁忌伤害定义为分割能匹配到最多的禁忌字符串数量(一个可以匹配多次),求由字符集构成的长度为Len的字符串的期望 ...
- BZOJ2553 Beijing2011禁忌(AC自动机+动态规划+矩阵快速幂+概率期望)
考虑对一个串如何分割能取得最大值.那么这是一个经典的线段覆盖问题,显然每次取右端点尽量靠前的串.于是可以把串放在AC自动机上跑,找到一个合法串后就记录并跳到根. 然后考虑dp.设f[i][j]表示前i ...
- BZOJ 2004 公交线路(状压DP+矩阵快速幂)
注意到每个路线相邻车站的距离不超过K,也就是说我们可以对连续K个车站的状态进行状压. 然后状压DP一下,用矩阵快速幂加速运算即可. #include <stdio.h> #include ...
- BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)
题目链接 先考虑 假设S确定,使构造S操作次数最小的方案应是:对T建SAM,S在SAM上匹配,如果有S的转移就转移,否则操作数++,回到根节点继续匹配S.即每次操作一定是一次极大匹配. 简单证明:假设 ...
- bzoj 1898: [Zjoi2005]Swamp 沼泽鳄鱼【dp+矩阵快速幂】
注意到周期234的lcm只有12,也就是以12为周期,可以走的状态是一样的 所以先预处理出这12个状态的转移矩阵,乘起来,然后矩阵快速幂优化转移k/12次,然后剩下的次数暴力转移即可 #include ...
- bzoj 2326: [HNOI2011]数学作业【dp+矩阵快速幂】
矩阵乘法一般不满足交换律!!所以快速幂里需要注意乘的顺序!! 其实不难,设f[i]为i的答案,那么f[i]=(f[i-1]w[i]+i)%mod,w[i]是1e(i的位数),这个很容易写成矩阵的形式, ...
- [poj2778 DNA Sequence]AC自动机,矩阵快速幂
题意:给一些字符串的集合S和整数n,求满足 长度为n 只含charset = {'A'.'T‘.'G'.'C'}包含的字符 不包含S中任一字符串 的字符串的种类数. 思路:首先对S建立ac自动机,考虑 ...
- BZOJ 4000: [TJOI2015]棋盘( 状压dp + 矩阵快速幂 )
状压dp, 然后转移都是一样的, 矩阵乘法+快速幂就行啦. O(logN*2^(3m)) ------------------------------------------------------- ...
随机推荐
- 【SSH框架】之Struts2系列(一)
微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系 1.Struts2框架概述 (1).什么是Struts2 Struts2是一种基于MVC模式的轻量 ...
- float精度丢失的问题
在做IPTV的时候,遇到以下这个问题: 现有一个float型数据,以下代码打印输出: float n = 40272.48f; System.out.println(new Double(n * 10 ...
- 奇异值分解(SVD)原理详解及推导 (转载)
转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/43053513 在网上看到有很多文章介绍SVD的,讲的也都不错,但是感觉还是有 ...
- 【Linux学习笔记】Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)
http://blog.csdn.net/slvher/article/details/8864996 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm. ...
- dat.gui 上手
dat.gui是款神器产品.一个调试利器.但是用起来很简单很简单 1:引用dat.gui.js. 2:实例化 this.gui = new dat.GUI(); 3:创建可设置一个数据对象.例如v ...
- PHP对象的遍历
对象的遍历 对象的遍历,跟数组的遍历,一样! 其实,只能遍历出对象的“实例属性数据” foreach( $对象名 as $key => $value){ //这里就可以处理$key和$va ...
- java和mysql的length()区别及char_length()
一. mysql里面的有length和char_length两个长度函数,区别在于: length: 一个汉字是算三个字符,一个数字或字母算一个字符. char_length: 不管汉字还是数字或者是 ...
- 【前端学习笔记】JavaScript JSON对象相关操作
//JSON方法 //JSON.parse(); var json = '{"name":"zj","age":23}'; JSON.par ...
- BZOJ 1565 植物大战僵尸(拓扑排序+最大权闭合子图)
图中的保护关系就类似于最大权闭合子图.即你想杀x,你就一定要杀掉保护x的点,那么把x向保护它的点连边.那么题目就转化成了最大权闭合子图的问题. 但是这个图有点特殊啊... 考虑有环的情况,显然这个环以 ...
- 【Java】自动获取某表某列的最大ID数
使用场景: 当需要往数据库插入数据时,表的主键需要接着已经有的数据后面进行自增.比如已经wq_customer表里,主键为TBL_ID,如果是空表,那么插入的数据TBL_ID设置为1,如果已经有n条数 ...