【题意】给定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+矩阵快速幂的更多相关文章

  1. Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂)

    Luogu-3250 [BJOI2017]魔法咒语(AC自动机,矩阵快速幂) 题目链接 题解: 多串匹配问题,很容易想到是AC自动机 先构建忌讳词语的AC自动机,构建时顺便记录一下这个点以及它的所有后 ...

  2. 【BZOJ】2553: [BeiJing2011]禁忌 AC自动机+期望+矩阵快速幂

    [题意]给定n个禁忌字符串和字符集大小alphabet,保证所有字符在集合内.一个字符串的禁忌伤害定义为分割能匹配到最多的禁忌字符串数量(一个可以匹配多次),求由字符集构成的长度为Len的字符串的期望 ...

  3. BZOJ2553 Beijing2011禁忌(AC自动机+动态规划+矩阵快速幂+概率期望)

    考虑对一个串如何分割能取得最大值.那么这是一个经典的线段覆盖问题,显然每次取右端点尽量靠前的串.于是可以把串放在AC自动机上跑,找到一个合法串后就记录并跳到根. 然后考虑dp.设f[i][j]表示前i ...

  4. BZOJ 2004 公交线路(状压DP+矩阵快速幂)

    注意到每个路线相邻车站的距离不超过K,也就是说我们可以对连续K个车站的状态进行状压. 然后状压DP一下,用矩阵快速幂加速运算即可. #include <stdio.h> #include ...

  5. BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)

    题目链接 先考虑 假设S确定,使构造S操作次数最小的方案应是:对T建SAM,S在SAM上匹配,如果有S的转移就转移,否则操作数++,回到根节点继续匹配S.即每次操作一定是一次极大匹配. 简单证明:假设 ...

  6. bzoj 1898: [Zjoi2005]Swamp 沼泽鳄鱼【dp+矩阵快速幂】

    注意到周期234的lcm只有12,也就是以12为周期,可以走的状态是一样的 所以先预处理出这12个状态的转移矩阵,乘起来,然后矩阵快速幂优化转移k/12次,然后剩下的次数暴力转移即可 #include ...

  7. bzoj 2326: [HNOI2011]数学作业【dp+矩阵快速幂】

    矩阵乘法一般不满足交换律!!所以快速幂里需要注意乘的顺序!! 其实不难,设f[i]为i的答案,那么f[i]=(f[i-1]w[i]+i)%mod,w[i]是1e(i的位数),这个很容易写成矩阵的形式, ...

  8. [poj2778 DNA Sequence]AC自动机,矩阵快速幂

    题意:给一些字符串的集合S和整数n,求满足 长度为n 只含charset = {'A'.'T‘.'G'.'C'}包含的字符 不包含S中任一字符串 的字符串的种类数. 思路:首先对S建立ac自动机,考虑 ...

  9. BZOJ 4000: [TJOI2015]棋盘( 状压dp + 矩阵快速幂 )

    状压dp, 然后转移都是一样的, 矩阵乘法+快速幂就行啦. O(logN*2^(3m)) ------------------------------------------------------- ...

随机推荐

  1. springMVC 流程

    springMVC流程控制 SpringMVC流程 web.xml 中配置 org.springframework.web.servlet.DispatcherServlet 这一步其实和spring ...

  2. 操作系统cmd

    实验一  命令解释程序的编写(两周内) 一.目的和要求 1. 实验目的 (1)掌握命令解释程序的原理: (2)*掌握简单的DOS调用方法: (3)掌握C语言编程初步. 2.实验要求 编写类似于DOS, ...

  3. 1."问吧APP"客户需求调查分析

    产品名称:问吧 产品功能:实时提问回答和搜索 开发原因:任何人都会遇到问题,网上虽然有很多回答,但是互联网的信息错综复杂,开发这个APP就是为了让网络求助更加的合理有效,清除网络上的垃圾信息. 为知大 ...

  4. C# 7.0 本地方法

    VS 2017 的 C# 7.0 中引入了本地方法,本地方法是一种语法糖,允许我们在方法内定义本地方法.更加类似于函数式语言,但是,本质上还是基于面向对象实现的. 1. 本地方法 先看一个示例: 1 ...

  5. FastReport.Net 无限页高(连续纸小票)

    using System; using System.Collections; using System.Collections.Generic; using System.ComponentMode ...

  6. 【HLSDK系列】怎么增加一种新实体

    你平常肯定接触到很多比如 info_player_start hostage info_target 之类的实体,这里就解释一下怎么创建一种新的实体. 首先建立一个新的 .h 文件(当然你写在现有的文 ...

  7. 🔺Count on a tree SPOJ - COT (无能为力。。。)

    https://cn.vjudge.net/problem/SPOJ-COT 插上 大佬的代码 和 我的...以后再看吧... Count on a tree 大佬:http://www.cnblog ...

  8. VSS2005设置不输入密码直接登录VSS

    1.登录管理员 2.Tools-->Options-->General -->Use network name for automatic user log in  去掉勾选不自动登 ...

  9. Hbase(四) 过滤器查询

    引言:过滤器的类型很多,但是可以分为两大类——比较过滤器,专用过滤器过滤器的作用是在服务端判断数据是否满足条件,然后只将满足条件的数据返回给客户端: 一.hbase过滤器的分类 1.比较过滤器 行键过 ...

  10. jar包下载地址(fasterxml.jackson)

    jar包下载地址(fasterxml.jackson) Home » com.fasterxml.jackson » core jar包下载地址 https://mvnrepository.com/a ...