【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)) ------------------------------------------------------- ...
随机推荐
- lintcode-464-整数排序 II
464-整数排序 II 给一组整数,按照升序排序.使用归并排序,快速排序,堆排序或者任何其他 O(n log n) 的排序算法. 样例 给出 [3, 2, 1, 4, 5], 排序后的结果为 [1, ...
- 软工网络15团队作业4-DAY3
昨天的工作. 张陈东芳:数据库连接的检查 吴敏烽:商品实体类的检查 周汉麟:继续研究获取商品信息方法的方法和调试 林振斌:继续研究获取商品信息方法的方法和调试 李智:Cookies的检查 全体人员:优 ...
- 爬虫学习之-Python list 和 str 互转
一.list转字符串 命令:''.join(list)其中,引号中是字符之间的分割符,如“,”,“;”,“\t”等等如:list = [1, 2, 3, 4, 5]''.join(list) 结果 ...
- 【linux】linux的数据流重定向
首先说一下什么是数据流重定向,所谓数据流重定向简单来说就是一个过程,这个过程捕捉一个文件,或者命令,程序,脚本,甚至脚本中的代码块(code block)的输出,然后把捕捉到的输出,作为输入发送给另外 ...
- 解决Mac下npm权限问题
前言 在学习Vue-CLI3的时候使用了全局安装,提示安装失败,本以为是npm版本问题,在更新npm的过程中又出现了 npm ERR! code: 'EACCES' ,查了一下发现是权限问题. 看到权 ...
- 51nod-1220-约数之和
题意 求 \[ \sum _{i=1}^n\sum _{j=1}^nd(ij) \\ d(x)=\sum _{e|x}e \] \(n\le 10^9\) . 分析 没有推出来.这题有几个要点要学习. ...
- C++——内存对象 禁止产生堆对象 禁止产生栈对象
用C或C++写程序,需要更多地关注内存,这不仅仅是因为内存的分配是否合理直接影响着程序的效率和性能,更为主要的是,当我们操作内存的时候一不小心就会出现问题,而且很多时候,这些问题都是不易发觉的,比如内 ...
- 解题:POI 2016 Nim z utrudnieniem
题面 出现了,神仙题! 了解一点博弈论的话可以很容易转化题面:问$B$有多少种取(diu)石子的方式使得取后剩余石子异或值为零且取出的石子堆数是$d$的倍数 首先有个暴力做法:$dp[i][j][k] ...
- 【bzoj4212】神牛的养成计划
Portal --> bzoj4212 Description 给你\(n\)个字符串,接下来有\(m\)个询问,每个询问由两个给定的字符串\(s_1\)和\(s_2\)组成,对于每个询问输 ...
- 专题训练之区间DP
例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http: ...