【题意】给定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. lintcode-203-线段树的修改

    203-线段树的修改 对于一棵 最大线段树, 每个节点包含一个额外的 max 属性,用于存储该节点所代表区间的最大值. 设计一个 modify 的方法,接受三个参数 root. index 和 val ...

  2. “献给爱读书的中国人”——Amazon Kindle软件测评

    “献给爱读书的中国人” ——Amazon Kindle软件测评 前不久我在网上看到了一篇印度工程师旅居上海时发表的一篇文章,题目叫做<令人忧虑:不阅读的中国人>,大致讲述的是世界上人们在飞 ...

  3. 2nd 词频统计效能测试

    词频统计效能测试 使用性能分析工具分析结果如下 :

  4. Java MD5加密类

    /************************************************* md5 类实现了RSA Data Security, Inc.在提交给IETF 的RFC1321中 ...

  5. QP(Quote-Printable) 编码

    QP(Quote-Printable)   方法,通常缩写为“Q”方法,其原理是把一个 8   bit   的字符用两个16进制数值表示,然后在前面加“=”.所以我们看到经过QP编码 后的文件通常是这 ...

  6. PHP执行原理

    简单解释:PHP执行原理 客户端向服务器发送一个请求,如果请求的是一个HTML页面,服务器直接将HTML页面发送到客户端给浏览器解析,如果请求的是PHP页面,则服务器会运行PHP页面然后生成标准的HT ...

  7. django-registration中的问题

    https://github.com/ubernostrum/django-registration.git https://django-registration.readthedocs.io/en ...

  8. vue render & array of components & vue for & vue-jsx

    vue render & array of components & vue for & vue-jsx https://www.cnblogs.com/xgqfrms/p/1 ...

  9. LoadRunner脚本增强技巧之检查点

    检查点的设置理解起来非常简单,就是要在服务器返回的页面中检查是否存在关键信息.检查点函数的错误会导致整个脚本运行结果的失败,通过这个功能可以方便地定位脚本运行中的逻辑错误.检查点的设置通常分为两种,一 ...

  10. HttpWebRequest和HttpWebResponse的应用

    创建使用类HttpHelper: public class Httpparam { public string UserAgent { get; set; } public string Accept ...