正题

题目链接:https://www.luogu.com.cn/problem/CF666E


解题思路

给出一个串\(S\)和\(n\)个串\(T_i\)。\(m\)次询问\(S_{a\sim b}\)在\(T_{l\sim r}\)中出现的最多次数并且输出这个串的编号。

\(1\leq |s|\leq 5\times 10^5,\sum T_i\leq 5\times 10^4,1\leq m\leq 5\times 10^5\)


解题思路

把\(S\)和\(T\)丢一起跑一个广义\(SAM\)。

两个串包含当且仅当他们在\(SAM\)上对应节点是父子,所以直接对于每个节点开一个线段树,然后\(T\)的每个位置对应编号加一。

对于询问\(S\)子串直接倍增跳到对应位置然后用线段树合并上来的东西求答案就好了。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+2e5+10,S=19;
int answ,ansv;
struct SegTree{
int cnt,w[N*20],v[N*20],ls[N*20],rs[N*20];
void PushUp(int x,int L,int R){
w[x]=-1;
if(w[ls[x]]>w[x])w[x]=w[ls[x]],v[x]=max(v[ls[x]],L);
if(w[rs[x]]>w[x])w[x]=w[rs[x]],v[x]=v[rs[x]];
return;
}
void Change(int &x,int L,int R,int pos){
if(!x)x=++cnt;
if(L==R){w[x]++;v[x]=L;return;}
int mid=(L+R)>>1;
if(pos<=mid)Change(ls[x],L,mid,pos);
else Change(rs[x],mid+1,R,pos);
PushUp(x,L,R);return;
}
void Ask(int x,int L,int R,int l,int r){
if(!x){if(answ<0)answ=0,ansv=l;return;}
if(L==l&&R==r){if(answ<w[x])answ=w[x],ansv=v[x];return;}
int mid=(L+R)>>1;
if(r<=mid)Ask(ls[x],L,mid,l,r);
else if(l>mid)Ask(rs[x],mid+1,R,l,r);
else Ask(ls[x],L,mid,l,mid),Ask(rs[x],mid+1,R,mid+1,r);
}
int Merge(int x,int y,int L,int R){
if(!x||!y)return x+y;int p=++cnt;
if(L==R){w[p]=w[x]+w[y];v[p]=L;return p;}
int mid=(L+R)>>1;
ls[p]=Merge(ls[x],ls[y],L,mid);
rs[p]=Merge(rs[x],rs[y],mid+1,R);
PushUp(p,L,R);return p;
}
}T;
struct node{
int to,next;
}a[N];
int n,m,l,tot,f[N][S+1],rt[N],ls[N];
int cnt,len[N],ch[N][26],fa[N],pos[N];
char s[N];
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
int Insert(int p,int c){
if(ch[p][c]){
int q=ch[p][c];
if(len[p]+1==len[q])return q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];fa[q]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
return nq;
}
}
int np=++cnt;
len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(len[p]+1==len[q])fa[np]=q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
return np;
}
void dfs(int x){
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
f[y][0]=x;dfs(y);
rt[x]=T.Merge(rt[x],rt[y],1,n);
}
return;
}
int GetPos(int l,int r){
int x=pos[r];
for(int i=S;i>=0;i--)
if(len[f[x][i]]>=r-l+1)x=f[x][i];
return x;
}
int main()
{
scanf("%s",s+1);
l=strlen(s+1);cnt=1;
for(int i=1,x=1;i<=l;i++){
x=Insert(x,s[i]-'a');
pos[i]=x;
}
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
int x=1,l=strlen(s+1);
for(int j=1;j<=l;j++){
x=Insert(x,s[j]-'a');
T.Change(rt[x],1,n,i);
}
}
for(int i=2;i<=cnt;i++)addl(fa[i],i);
dfs(1);
for(int j=1;j<=S;j++)
for(int i=1;i<=cnt;i++)
f[i][j]=f[f[i][j-1]][j-1];
scanf("%d",&m);
while(m--){
int a,b,l,r;
scanf("%d%d%d%d",&l,&r,&a,&b);
int x=GetPos(a,b);answ=-1;ansv=0;
T.Ask(rt[x],1,n,l,r);
printf("%d %d\n",ansv,answ);
}
return 0;
}

CF666E-Forensic Examination【广义SAM,线段树合并】的更多相关文章

  1. CodeForces - 666E: Forensic Examination (广义SAM 线段树合并)

    题意:给定字符串S,然后M个字符串T.Q次询问,每次给出(L,R,l,r),问S[l,r]在L到R这些T字符串中,在哪个串出现最多,以及次数. 思路:把所有串建立SAM,然后可以通过倍增走到[l,r] ...

  2. CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线

    传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...

  3. 【Codeforces666E】Forensic Examination 后缀自动机 + 线段树合并

    E. Forensic Examination time limit per test:6 seconds memory limit per test:768 megabytes input:stan ...

  4. CF204E-Little Elephant and Strings【广义SAM,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/CF204E 题目大意 \(n\)个字符串的一个字符串集合,对于每个字符串求有多少个子串是这个字符串集合中至少\(k\ ...

  5. YbtOJ#532-往事之树【广义SAM,线段树合并】

    正题 题目链接:https://www.ybtoj.com.cn/problem/532 题目大意 给出\(n\)个点的一个\(Trie\)树,定义\(S_x\)表示节点\(x\)代表的字符串 求$$ ...

  6. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  7. CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增

    题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ...

  8. CF666E Forensic Examination——SAM+线段树合并+倍增

    RemoteJudge 题目大意 给你一个串\(S\)以及一个字符串数组\(T[1...m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l...p_r]\)在\(T[l...r]\)中的 ...

  9. CF666E Forensic Examination SAM+线段树合并+前缀树倍增

    $ \color{#0066ff}{ 题目描述 }$ 给你一个串\(S\)以及一个字符串数组\(T[1..m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l..p_r]\)在\(T[l. ...

随机推荐

  1. c#创建windows服务入门教程实例

    用c#中创建一个windows服务非常简单,与windows服务相关的类都在System.ServiceProcess命名空间下. 每个服务都需要继承自ServiceBase类,并重写相应的启动.暂停 ...

  2. .NET 元数据概述

    元数据是一种二进制信息,用以对存储在公共语言运行库可移植可执行文件 (PE) 文件或存储在内存中的程序进行描述.将您的代码编译为 PE 文件时,便会将元数据插入到该文件的一部分中,而将代码转换为 Mi ...

  3. ajax传字符串时出现乱码问题的解决

    字符乱码的解决: 第一种在@RequestMapping中添加 @RequestMapping(value="queryAllToTree",method=RequestMetho ...

  4. springmvc学习日志四

    一.回顾 1.文件上传 1.1引入fileupload的jar包 1.2在springmvc的配置文件中引入CommonsMutilpartResolver文件上传解析器 1.3在控制层在写入代码 2 ...

  5. 关于windows下 python3安装 cython的说明

    针对python3.6希望在windows环境下安装cython,但是网上任何关于mingw的尝试都没有生效.所以只能下载 vs, 1.去官网https://visualstudio.microsof ...

  6. 未解决:为什么在struts2下新建ognl的包,会出错?

    首先开始在src下新建了一个名叫ognl的包: 发现在其中放置了一个loginAction,即使是最简单的跳转都不能实现: 直接抛出了java.lang.Exception; 传递参数更出现了异常: ...

  7. Python - 面向对象编程 - 类变量、实例变量/类属性、实例属性

    什么是对象和类 https://www.cnblogs.com/poloyy/p/15178423.html 什么是 Python 类.类对象.实例对象 https://www.cnblogs.com ...

  8. spring 》Cglib赋值

    第一个:字节码文件时带有ENHANCERBYCGLIB,FastClassByCGLIB组成的文件名 第二个:字节码文件时带有ENHANCERBYCGLIB 第三个:字节码文件时带有FastClass ...

  9. Nginx版本平滑升级方案

    背景:由于负载均衡测试服务器中nginx版本过低,存在安全漏洞,查询相关修复漏洞资料,需要采取nginx版本升级形式对漏洞进行修复. Nginx平滑升级方案 1.案例采用版本介绍 旧版本 nginx- ...

  10. 菜鸟入门Linux之路(方法论浅谈)

    Linux是为人熟知的OS之王,已"统治"世界.要想学好绝非易事. 作为菜鸟,可以与Linux亲密接触的方法很多,如视频.书籍.各种企培资料等等,如今的在线教育也如火如荼. 总结说 ...