CF666E Forensic Examination(后缀自动机+动态线段树)
题意
给你一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l..p_r]$ 在 $T[l..r]$ 中的哪个串里的出现次数最多,并输出出现次数。如有多解输出最靠前的那一个。
题解
神仙题……虽然的确是好题……然而码量好大……好麻烦……因为一个sb错误调了一个下午
先膜一波zsy大佬……%%%
首先先把$T$给建出一个广义$SAM$。考虑每一个询问的$p_r$,如果在$SAM$上匹配到了一个节点,那么这个子串$S[p_l,p_r]$一定是这个节点的一个祖先(然后如果根本匹配不到的话直接跳过)
然后可以考虑倍增找到祖先,只要找到满足$len_u>=p_r-p_l+1$的最上面的节点就好了
然后考虑如何表示这个节点在$[l,r]$范围内在哪些串出现了多少次,以及出现次数的最大值。区间查询可以考虑用线段树。对于每一个$SAM$上的节点,我们对他建一棵线段树,表示有这个节点代表的字符串在$T$中的出现次数。然后通过倍增找到节点,在线段树上查询就是了
于是直接把串$S$放到$SAM$上跑,然后在每一个节点记录一下有哪些询问在这个点上,一波$dfs$的时候顺便合并父亲和儿子的出现次数的区间,并求出答案
说的好像很简单……然而真的码得有点想哭……
//minamoto
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while((ch=getchar())<''||ch>'')
(ch=='-')&&(flag=true);
for(res=num;(ch=getchar())>=''&&ch<='';res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(int x,char ch){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]=ch;
}
const int N=5e5+;
struct data{
int x,y;
inline bool operator <(const data &b)const
{return x<b.x||x==b.x&&y>b.y;}
}ans[N];
int L[N*],R[N*];data V[N*];
struct node{int l,r,pl,pr;}q[N];
int n,m,Q,last=,cnt=,ch[N][],fa[][N],l[N],rt[N],tot=;
int nxt[][N],head[][N];
char s[N],t[N];
void ins(int c){
int p=last,np=++cnt;last=np,l[np]=l[p]+;
for(;p&&!ch[p][c];p=fa[][p]) ch[p][c]=np;
if(!p) fa[][np]=;
else{
int q=ch[p][c];
if(l[q]==l[p]+) fa[][np]=q;
else{
int nq=++cnt;l[nq]=l[p]+;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[][nq]=fa[][q],fa[][q]=fa[][np]=nq;
for(;ch[p][c]==q;p=fa[][p]) ch[p][c]=nq;
}
}
}
void modify(int &now,int l,int r,int x){
now=++tot;
if(l==r) return (void)(++V[now].x,V[now].y=l);
int mid=l+r>>;
if(x<=mid) modify(L[now],l,mid,x);
else modify(R[now],mid+,r,x);
V[now]=max(V[L[now]],V[R[now]]);
}
void merge(int &x,int y){
if(!x||!y) return (void)(x|=y);
if(!L[x]&&!R[x]) return (void)(V[x].x+=V[y].x);
merge(L[x],L[y]),merge(R[x],R[y]);
V[x]=max(V[L[x]],V[R[x]]);
}
data query(int p,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r) return V[p];
int mid=l+r>>;
if(qr<=mid) return query(L[p],l,mid,ql,qr);
if(ql>mid) return query(R[p],mid+,r,ql,qr);
return max(query(L[p],l,mid,ql,qr),query(R[p],mid+,r,ql,qr));
}
void dfs(int u){
for(int i=head[][u];i;i=nxt[][i])
dfs(i),merge(rt[u],rt[i]);
for(int i=head[][u];i;i=nxt[][i])
ans[i]=query(rt[u],,m,q[i].l,q[i].r);
}
int main(){
scanf("%s",s+);n=strlen(s+);
m=read();
for(int i=;i<=m;++i){
scanf("%s",t+);int len=strlen(t+);last=;
for(int j=;j<=len;++j) ins(t[j]-'a'),modify(rt[last],,m,i);
}
Q=read();
for(int i=;i<=Q;++i){
int a=read(),b=read(),c=read(),d=read();
q[i]=(node){a,b,c,d};
nxt[][i]=head[][q[i].pr],head[][q[i].pr]=i;
}
for(int i=;i<=cnt;++i)
nxt[][i]=head[][fa[][i]],head[][fa[][i]]=i;
for(int j=;j<;++j)
for(int i=;i<=cnt;++i)
fa[j][i]=fa[j-][fa[j-][i]];
for(int i=,now=,cnt=;i<=n;++i){
while(now&&!ch[now][s[i]-'a']) now=fa[][now],cnt=l[now];
if(!now){now=,cnt=;continue;}
now=ch[now][s[i]-'a'],++cnt;
for(int j=head[][i];j;j=nxt[][j]){
int u=now;if(cnt<q[j].pr-q[j].pl+) continue;
for(int k=;~k;--k)
if(l[fa[k][u]]>=q[j].pr-q[j].pl+)
u=fa[k][u];
nxt[][j]=head[][u],head[][u]=j;
}
}
dfs();
for(int i=;i<=Q;++i){
if(!ans[i].x) ans[i].y=q[i].l;
print(ans[i].y,),print(ans[i].x,);
}
Ot();
return ;
}
CF666E Forensic Examination(后缀自动机+动态线段树)的更多相关文章
- CF666E Forensic Examination [后缀自动机,线段树合并]
		洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ... 
- CF666E Forensic Examination SAM+倍增,线段树和并
		题面: 给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数.如有多解输出最靠前的那一个. 分析: 第 ... 
- CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线
		传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ... 
- 【CF666E】Forensic Examination(后缀自动机,线段树合并)
		[CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ... 
- 【BZOJ3413】匹配(后缀自动机,线段树合并)
		[BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ... 
- 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并
		[CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ... 
- CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增
		题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ... 
- 【Codeforces666E】Forensic Examination      后缀自动机 + 线段树合并
		E. Forensic Examination time limit per test:6 seconds memory limit per test:768 megabytes input:stan ... 
- CF 666E Forensic Examination 【SAM 倍增 线段树合并】
		CF 666E Forensic Examination 题意: 给出一个串\(s\)和\(n\)个串\(t_i\),\(q\)次询问,每次询问串\(s\)的子串\(s[p_l:p_r]\)在串\(t ... 
随机推荐
- go_指针
			值传递:相当于在内存中拷贝一分变量出来,拷贝变量的改变对原变量不影响 引用传递:直接引用内存中的变量,会被改变 c,c++值传递和引用传递都有 Java,python多用引用传递 go语言指针简单之处 ... 
- Nginx实现ssl一级、二级域名证书部署并用https访问代理转发服务器
			1. 规划 域名 解析IP Nginx代理 htpps://www.devcult.com 47.88.10.155 htpps://auto.devcult.com 47.88.10.155 ... 
- 快速上手Runtime(二)之给分类添加属性
			我们都知道,分类是不能直接添加属性的,那么我们有时候又需要实现这个功能,那么我们应该怎么办才能为分类添加上属性呢. Runtime给分类添加属性原理 给一个类声明属性,其实本质就是给这个类添加关联,并 ... 
- 推荐一个好用的sql版本控制和美化工具SQLToolbelt
			SQL Toolbelt含Red Gate公司目前出品的所有SQL Server工具和新版本.也就是说,当你使用SQL Server数据库工作时,您可以很方便的获得您所需要的一套强大的工具,包括SQL ... 
- Jmeter线程ramp-up period (in seconds)如何取值
			线程组主要包含三个参数:线程数.准备时长(Ramp-Up Period(in seconds)).循环次数. 线程数:虚拟用户数.一个虚拟用户占用一个进程或线程.设置多少虚拟用户数在这里也就是设置多少 ... 
- javascript中的replace()方法
			javascript中有很多很好用的操作字符串和数组的方法,replace()就是其中一个. 看看基本用法 var str = '1234512345'; var newStr = str.repla ... 
- Boost线程详解
			一.创建一个线程 创建线程 boost::thread myThread(threadFun); 需要注意的是:参数可以是函数对象或者函数指针.并且这个函数无参数,并返回void类型. 当一个thre ... 
- vscode安装dlv插件报错:There is no tracking information for the current branch.
			vscode安装dlv插件报错:There is no tracking information for the current branch. https://blog.csdn.net/a7859 ... 
- CentOS 7 更换 阿里云/清华大学 yum 软件源
			阿里云参考:https://opsx.alibaba.com/mirror?lang=zh-CN 清华参考:https://mirrors.tuna.tsinghua.edu.cn/help/cent ... 
- zigbee广播通信原理
			广播:可以理解成,发送模块发出数据,这个网络里的所有节点模块都可以拿到这个数据. 实验:终端模块以广播的形式发送出去,让协调器和路由器模块作为接收器收到数据并显示出来! 协调器模块作为接收模块: 和单 ... 
