bzoj 2780
后缀自动机的应用
首先我们观察到:如果一个询问串的答案不为0,那么这个串一定是至少一个模式串的子串
如果只有一个模式串,那么这个问题可以简单地用什么东西解决掉(比如普通后缀自动机)
而这里有很多模式串,所以普通后缀自动机是不够的。
那么我们提出广义后缀自动机
所谓广义后缀自动机,可以简单理解成将很多个串建在同一个后缀自动机上
所以它的构造就是:每插入完毕一个串,就将las指针指回根节点,然后去构造下一个串就可以了
好像很简单?
上面的构造方法是不准确的!
这里转载一位dalao的博客,他详尽地给出了如上的构造方法的错误之处以及正确的方法
(但是太恶心了,而且一般错误的方法也不会被卡,所以我们这里还是使用了错误方法,但是在这里要有个印象,这并不是完全正确的构造方法)
接下来是转载部分:
"由于之前的疏忽,一直认为广义后缀自动机的构建方法就是在普通后缀自动机上直接插入多串,在此修正
原先的方法:对第一个串建后缀自动机,在插入第二个串时将fin指针指向根,然后暴力插入第二个字符串。
这种方法的错误:
当原来的字符串集合中含有这个字符串时,便会多建立新点,这个点并没有被任何点的tranc指向。
为什么之前一直没出问题:
我做题太少了
在大多数情况下,这个点是不会更新且不会被更新的,而且它前后的点都表现正常。
所以,在所有串都是静态的,就是一遍建成在大多数情况下是不会出现问题的。
那什么时候出现的问题:
当这个字符串集合是动态的(只插入不删除)时,一般我们使用LCT来维护Parent树。
这时,当我们拎出链进行修改时这个点就会参与运算,(然而这个点还没有记录原来修改的值)所以重复字符串/字符串前缀是需要在建立广义后缀自动机时进行特判处理
广义后缀自动机的本质:
普通后缀自动机是依靠字符串建立起来的。
而广义后缀自动机原则上是在原字符串集合的trie树上建立的,原则上要先建出trie树,然后记录每个点的fin指针。
每个节点在构建时需要在父亲的fin上构造。
原则上trie树的遍历可以使用dfs和bfs,但是dfs可以被构造的数据卡成O(n2)O(n2)
所以原则上要使用bfs来建广义后缀自动机。
但是考虑我们要解决的问题,也就是trie上构造和直接插入本质上的区别,就是trie树省略了前缀的重复
从这个性质入手,可以直接将后缀树的节点破开(特判一下就好了,就是前缀重复时像trie一样搞就好了)”
转载部分结束
接下来进入正题
我们还是按照老方法构造广义后缀自动机,本题不会被卡
然后我们分析一下题意:
如果一个询问串是一个模式串的子串,那么这个询问串一定是这个模式串的一个前缀的后缀(虽然我们常用的定义是后缀的前缀,但是在这个“后缀自动机”里我们使用第一个定义更容易理解)
那么,基于后缀自动机的pre指针的定义,我们发现:一个pre指针指向的点所对应的串一定是原节点对应字符串的一个后缀!
那么,如果我们将pre指针反指,就会得到一个树形结构,我们称这棵树叫parent树
我们可以发现,parent树的一个父节点对应的串是它所有子代节点(即儿子,儿子的儿子...)所对应串的子串!
以上内容,是在学习后缀自动机时应当了解到的,其实是基础知识,但是还是要介绍一下
那么对于这道题,我们发现:考虑到如果答案不为0,那么后缀自动机一定能识别这个串
所以我们可以先用建好的后缀自动机去识别每一个串,以此就可以查出所有答案为0的部分
然后,我们记录下答案不为0的询问串的结束位置,然后我们建起parent树求出dfs序,那么我们只需求出对于每个结束位置,它在parent树上的子树内有多少个不同的endpos即可(在广义后缀自动机上,用不同的endpos区分不同的串)
这一点可以利用dfs序实现
具体来讲,求出parent树的dfs之后,基于dfs序的性质,问题就转变成了在一段区间内(即询问节点的入栈序与出栈序)之间不同数值的个数
那么这类似于bzoj 1878,HH的项链
我们只需离线所有询问,然后用树状数组搞就可以了。
具体看代码
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct SAM
{
int tranc[27];
int endpos;
int pre;
int len;
}s[200005];
struct Edge
{
int next;
int to;
}edge[200005];
struct Ques
{
int lq,rq,num;
}q[60005];
int cnt=1;
int n,m;
char ch[360005];
int head[200005];
int inr[200005];
int our[200005];
int sum[400005];
int last[200005];
int ret[60005];
int f[400005];
int las,siz;
int dep;
int tot;
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
bool cmp(Ques x,Ques y)
{
return x.rq<y.rq;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int l,int r)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
head[l]=cnt++;
}
void update(int x,int y)
{
while(x<=dep)
{
sum[x]+=y;
x+=lowbit(x);
}
}
int get_sum(int x)
{
int ans=0;
while(x)
{
ans+=sum[x];
x-=lowbit(x);
}
return ans;
}
void ins(int c,int typ)
{
int nwp=++siz;
s[nwp].endpos=typ;
s[nwp].len=s[las].len+1;
int lsp;
for(lsp=las;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)s[lsp].tranc[c]=nwp;
if(!lsp)
{
s[nwp].pre=1;
}else
{
int lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+1)
{
s[nwp].pre=lsq;
}else
{
int nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+1;
s[nwq].endpos=0;
s[lsq].pre=s[nwp].pre=nwq;
while(s[lsp].tranc[c]==lsq)s[lsp].tranc[c]=nwq,lsp=s[lsp].pre;
}
}
las=nwp;
}
void buildtree()
{
init();
for(int i=2;i<=siz;i++)add(s[i].pre,i);
}
void dfs(int x)
{
inr[x]=++dep;
f[dep]=x;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
dfs(to);
}
our[x]=++dep;
}
int check(int l)
{
int laas=1;
for(int i=1;i<=l;i++)
{
if(s[laas].tranc[ch[i]-'a'+1])laas=s[laas].tranc[ch[i]-'a'+1];
else return 0;
}
return laas;
}
int main()
{
scanf("%d%d",&n,&m);
las=++siz;
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
int len=strlen(ch+1);
for(int j=1;j<=len;j++)ins(ch[j]-'a'+1,i);
las=1;
}
buildtree();
dfs(1);
for(int i=1;i<=m;i++)
{
scanf("%s",ch+1);
int len=strlen(ch+1);
int t=check(len);
if(t)
{
q[++tot].lq=inr[t];
q[tot].rq=our[t];
q[tot].num=i;
}
}
sort(q+1,q+tot+1,cmp);
int ttop=1;
for(int i=1;i<=dep;i++)
{
update(i,1);
if(last[s[f[i]].endpos])update(last[s[f[i]].endpos],-1);
last[s[f[i]].endpos]=i;
while(q[ttop].rq==i)
{
ret[q[ttop].num]=get_sum(q[ttop].rq)-get_sum(q[ttop].lq-1)-1;
ttop++;
}
}
for(int i=1;i<=m;i++)printf("%d\n",ret[i]);
return 0;
}
bzoj 2780的更多相关文章
- 三种做法:BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster
目录 题意 思路 AC_Code1 AC_Code2 AC_Code3 参考 @(bzoj 2780: [Spoj]8093 Sevenk Love Oimaster) 题意 链接:here 有\(n ...
- BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )
全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries ...
- bzoj 3277 串 && bzoj 3473 字符串 && bzoj 2780 [Spoj]8093 Sevenk Love Oimaster——广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...
- bzoj 3277 & bzoj 3473,bzoj 2780 —— 广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...
- BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster [广义后缀自动机]
JZPGYZ - Sevenk Love Oimaster Oimaster and sevenk love each other. But recently,sevenk hea ...
- BZOJ.2780.[SPOJ8093]Sevenk Love Oimaster(广义后缀自动机)
题目链接 \(Description\) 给定n个模式串,多次询问一个串在多少个模式串中出现过.(字符集为26个小写字母) \(Solution\) 对每个询问串进行匹配最终会达到一个节点,我们需要得 ...
- 【刷题】BZOJ 2780 [Spoj]8093 Sevenk Love Oimaster
Description Oimaster and sevenk love each other. But recently,sevenk heard that a girl named ChuYuXu ...
- bzoj 2780: [Spoj]8093 Sevenk Love Oimaster(广义SAM)
题目大意:给出n个原串,再给出m个查询串.求每个查询串出现在了多少原串中. 题解 直接对原串建一个广义SAM,然后把每一个原串放到SAM上跑一跑,记录一下每一个状态属于多少个原串,用$size$表示. ...
- BZOJ 2780 [Spoj]8093 Sevenk Love Oimaster ——广义后缀自动机
给定n个串m个询问,问每个串在n个串多少个串中出现了. 构建广义后缀自动机,(就是把所有字符串的后缀自动机合并起来)其实只需要add的时候注意一下就可以了. 然后对于每一个串,跑一边匹配,到达了now ...
随机推荐
- js01-javascript语法标准和数据类型
语法规则 (1)JavaScript对换行.缩进.空格不敏感. 备注:每一条语句末尾要加上分号,虽然分号不是必须加的,但是为了程序今后要压缩,如果不加分号,压缩之后将不能运行. (2)所有的符号,都是 ...
- spring cloud实战与思考(三) 微服务之间通过fiegn上传一组文件(下)
需求场景: 用户调用微服务1的接口上传一组图片和对应的描述信息.微服务1处理后,再将这组图片上传给微服务2进行处理.各个微服务能区分开不同的图片进行不同处理. 上一篇博客已经讨论了在微服务之间传递一组 ...
- 操作docker容器
Docker容器时镜像的一个运行实例,而镜像是静态的只读文件,容器带有运行时需要的可写文件层.如果认为虚拟机是模拟运行的一整套操作系统(包括内核.应用运行的环境和其他系统环境)和跑在上面的应用,那么D ...
- Ubuntu16.04安装Redis并配置
Ubuntu16.04安装Redis并配置 2018年05月22日 10:40:35 Hello_刘 阅读数:29146 Ubuntu16.04安装Redis并配置 1):安装: 1:终端命令下载 ...
- 洛谷 P2257 【YY的GCD】
这道题还是和上一道[ZAP]有那么一点点的相似哈 题目大意 给定N, M,求1<=x<=N, 1<=y<=M且\(gcd(x, y)\)为质数的(x, y)有多少对 如果对莫比 ...
- node.js的基础知识
第一部分知识: .命令行窗口(小黑屏).CMD窗口.终端.shell - 开始菜单 --> 运行 --> CMD --> 回车 - 常用的指令: dir 列出当前目录下的所有文件 c ...
- 第七节:语法总结(1)(自动属性、out参数、对象初始化器、var和dynamic等)
一. 语法糖简介 语法糖也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方 ...
- SpringBoot系列: 使用 Swagger 生成 API 文档
SpringBoot非常适合开发 Restful API程序, 我们都知道为API文档非常重要, 但要维护好难度也很大, 原因有: 1. API文档如何能被方便地找到? 以文件的形式编写API文档都有 ...
- SpringBoot系列: RestTemplate 快速入门
====================================相关的文章====================================SpringBoot系列: 与Spring R ...
- day 13 - 1 迭代器
迭代器 首先我们查看下列类型拥有的所有方法(会显示很多) print(dir([])) print(dir({})) print(dir('')) print(dir(range(10))) #求下上 ...