BZOJ3413: 匹配(后缀自动机,Parent树,线段树合并)
Description
Input
第一行包含一个整数n(≤100000)。
第二行是长度为n的由0到9组成的字符串。
第三行是一个整数m。
接下来m≤5·10行,第i行是一个由0到9组成的字符串s,保证单行字符串长度小于等于10^5,所有字符串长度和小于等于3·10^6
Output
输出m行,第i行表示第si和S匹配所比较的次数。
Sample Input
1090901
4
87650
0901
109
090
Sample Output
10
3
4
解题思路:
卡了我一天,我好弱啊
这道题需要在思路上做出一步转化,将分配次数分配到每个点上。
话句话说,假如说在模板串i位上匹配了f[i]次,那么答案就是sigma(f[i])。
那么就要求我们求出在每一位上的f[i]的值。
f[i]=在i上失配次数+在i上匹配成功次数,其中失配次数可以单独处理出来。
如果匹配永远不会成功,那么,我们可以知道,失配次数一定是n。
如果匹配在某一位成功,那么失配次数就是左端点右移次数。
那么成功次数呢?
可知,在完成匹配之后的部分,是不产生贡献的。
那么结果就是在一个Parent节点子树内的结束节点。
所以我们就需要维护一个集合,集合内包含所有Parent节点子树内的endpos。
这部分用线段树就好了,向上合并。
查询个数时只需要确定好上限。
查询结束位置时只需要进行后缀自动机匹配即可,记录最后一个节点并保证未失配。
再进行第二次匹配,只需要限制其上限,在线段树中查询个数就好了。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=;
const int S=;
typedef long long lnt;
struct sant{
int tranc[];
int len;
int pre;
}s[S];
struct pnt{
int hd;
int root;
}p[S];
struct trnt{
int ls;
int rs;
int sum;
}tr[S<<];
int cnt;
int siz;
int fin;
int n,Q;
int top;
int size;
char tmp[N];
int topo[S];
int has[S];
int t;
void pushup(int spc)
{
tr[spc].sum=tr[tr[spc].ls].sum+tr[tr[spc].rs].sum;
return ;
}
int hav(int l,int r,int spc,int pos)
{
if(!spc)
return ;
if(l==r)
return l;
int mid=(l+r)>>;
if(pos<=mid)
return hav(l,mid,tr[spc].ls,pos);
else
return hav(mid+,r,tr[spc].rs,pos);
}
void update(int l,int r,int &spc,int pos)
{ if(!spc)
spc=++size;
tr[spc].sum++;
if(l==r)
return ;
int mid=(l+r)>>;
if(pos<=mid)
update(l,mid,tr[spc].ls,pos);
else
update(mid+,r,tr[spc].rs,pos);
return ;
}
int Merge(int spcf,int spcs)
{
if(!spcf||!spcs)
return spcf+spcs;
int spc=++size;
tr[spc].sum=tr[spcf].sum+tr[spcs].sum;
tr[spc].ls=Merge(tr[spcf].ls,tr[spcs].ls);
tr[spc].rs=Merge(tr[spcf].rs,tr[spcs].rs);
return spc;
}
int Minpos(int l,int r,int spc)
{
if(l==r)
return l;
int mid=(l+r)>>;
if(tr[tr[spc].ls].sum)
return Minpos(l,mid,tr[spc].ls);
else
return Minpos(mid+,r,tr[spc].rs);
}
lnt query(int l,int r,int spc,int lim)
{
if(l>lim||!spc)
return ;
if(l==r||r<=lim)
return tr[spc].sum;
int mid=(l+r)>>;
return query(l,mid,tr[spc].ls,lim)+query(mid+,r,tr[spc].rs,lim);
}
void Insert(int c,int plc)
{
t=plc;
int nwp,nwq,lsp,lsq;
nwp=++siz;
s[nwp].len=s[fin].len+;
for(lsp=fin;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)
s[lsp].tranc[c]=nwp;
if(!lsp)
s[nwp].pre=;
else{
lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+)
s[nwp].pre=lsq;
else{
nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+;
s[lsq].pre=s[nwp].pre=nwq;
while(s[lsp].tranc[c]==lsq)
{
s[lsp].tranc[c]=nwq;
lsp=s[lsp].pre;
}
}
}
fin=nwp;
update(,n,p[fin].root,plc);
return ;
}
int main()
{
fin=++siz;
scanf("%d",&n);
scanf("%s",tmp+);
for(int i=;i<=n;i++)
Insert(tmp[i]-'',i);
for(int i=;i<=siz;i++)
has[s[i].len]++;
for(int i=;i<=siz;i++)
has[i]+=has[i-];
for(int i=;i<=siz;i++)
topo[has[s[i].len]--]=i;
for(int i=siz;i;i--)
{
int x=topo[i];
if(x==)
continue;
p[s[x].pre].root=Merge(p[s[x].pre].root,p[x].root);
}
scanf("%d",&Q);
while(Q--)
{
scanf("%s",tmp+);
int len=strlen(tmp+);
int root=;
int endpos=0x3f3f3f3f;
lnt ans=;
for(int i=;i<=len;i++)
root=s[root].tranc[tmp[i]-''];
if(!root)
ans=n;
else{
endpos=Minpos(,n,p[root].root);
ans=endpos-len;
}
root=;
for(int i=;i<=len;i++)
{
int c=tmp[i]-'';
root=s[root].tranc[c];
int tmp=ans;
if(root)
ans+=query(,n,p[root].root,endpos+i-len);
else
break;
}
printf("%lld\n",ans);
}
return ;
}
BZOJ3413: 匹配(后缀自动机,Parent树,线段树合并)的更多相关文章
- [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)
题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+ ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心
题目描述: 给定一个字符串 $S$ 给出 $Q$ 个操作,给出 $L,R,T$,求出字典序最小的 $S_{1}$ 为 $S[L...R]$的子串,且 $S_{1}$ 的字典序严格大于 $T$. 输出这 ...
- CF700E Cool Slogans 后缀自动机 + right集合线段树合并 + 树形DP
题目描述 给出一个长度为n的字符串s[1],由小写字母组成.定义一个字符串序列s[1....k],满足性质:s[i]在s[i-1] (i>=2)中出现至少两次(位置可重叠),问最大的k是多少,使 ...
- 浅谈树套树(线段树套平衡树)&学习笔记
0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...
- BZOJ 3413 匹配 (后缀自动机+线段树合并)
题目大意: 懒得概括了 神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题 线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个 ...
- [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增
题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...
- Luogu3732 [HAOI2017] 供给侧改革 【后缀数组】【线段树】【乱搞】
题目分析: 这道题我是乱搞的,因为他说$01$串是随机的. 那么我们可以猜测能够让LCP变大的地方很少.求出后缀数组之后可能让LCP变大的地方就等价于从大到小往height里动态加点同时维护这个点左右 ...
- Luogu5289 十二省联考2019字符串问题(后缀数组+拓扑排序+线段树/主席树/KDTree)
先考虑80分做法,即满足A串长度均不小于B串,容易发现每个B串对应的所有A串在后缀数组上都是一段连续区间,线段树优化连边然后判环求最长链即可.场上就写了这个. 100分也没有什么本质区别,没有A串长度 ...
随机推荐
- JDBC程序实例
实例 ( Statement ): public class JDBC { public static void main(String[] args) throws Exception { Conn ...
- what is udev?
如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...
- DOM元素属性值如果设置为对象
结论:内部会调用toString方法,将设置的对象转换为字符串添加给相应的属性: 这个问题呢,是通过jQuery的each方法中,回调函数的this指向问题而来: 我们知道,回调函数中的this如果指 ...
- JS一个经典闭包问题
这里是记录一些本人在学习过程中觉得重要的知识点,记录下来以供日后查看,如有不对欢迎指正,望在前端的路上共勉! <!DOCTYPE html> <html lang="en& ...
- Swoole 源码分析——进程管理 Swoole_Process
前言 swoole-1.7.2 增加了一个进程管理模块,用来替代 PHP 的 pcntl 扩展. PHP自带的pcntl,存在很多不足,如 pcntl 没有提供进程间通信的功能 pcntl 不支持重定 ...
- 全面的framebuffer详解
一.FrameBuffer的原理 FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口. Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIO ...
- [luogu] P3202 [HNOI2009]通往城堡之路(贪心)
P3202 [HNOI2009]通往城堡之路 题目描述 听说公主被关押在城堡里,彭大侠下定决心:不管一路上有多少坎坷,不管城堡中的看守有多少厉害,不管救了公主之后公主会不会再被抓走,不管公主是否漂亮. ...
- H5新增的标签总结
1:便于排版的Form表单 HTML5为了方便排版,可以使form的表单标签脱离form的嵌套. 方法:form指定id,所有的标签标签都添加form= id属性. 例如: <form acti ...
- FastDFS架构
1.什么是 FastDFS FastDFS是用c语言编写的一款开源的分布式文件系统.FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高性能等指标,使用Fas ...
- 2.WHERE中使用=,>,>=,<,<=,<>,!=比较符号
//查询工资大于等于2000的人 select * from person salary >= 2000; //查询名字等于scott的人 select * from per ...