给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数。

如有多解输出最靠前的那一个。

我们首先对m个字符串数组建出后缀自动机,然后我们可以通过跳trans边找到S前i个字符代表的前缀的最长后缀。我们要找的是S[pl..pr]并不是以pr结束最长的后缀,但我们可以确定S[pl..pr]一定是当前点的祖先所以当我们跳到pr代表的点时我们倍增往上跳知道找到一个点的长度刚好大于等于pr-pl+1,这个点就是询问区间代表的点。

那么我们怎么求答案呢?上线段树合并就行(线段树以[1,m]为值域),这就要求我们对询问离线。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=501000;
int n,m,L;
char S[N],s[N];
int cnt,head[N];
struct edge{
int to,nxt;
}e[N];
void add(int u,int v){
cnt++;
e[cnt].nxt=head[u];
e[cnt].to=v;
head[u]=cnt;
}
struct ques{
int l,a,b,id;
ques(int ll=0,int aa=0,int bb=0,int idx=0){
l=ll;a=aa;b=bb;id=idx;
}
};
struct data{
int mx,id;
data(int mxx=0,int idx=0){
mx=mxx;id=idx;
}
}ans[N];
struct qu{
int a,b,id;
qu(int idx=0,int aa=0,int bb=0){
a=aa;b=bb;id=idx;
}
};
vector<qu> que[N];
data max(data a,data b){
if(a.mx==b.mx){
if(a.id<b.id)return a;
return b;
}
if(a.mx>b.mx)return a;
return b;
}
vector<ques> vec[N];
struct sam{
int tot,u,trans[N][27],fa[N][23],len[N];
int root[N],ch[N*50][2],cnt,mx[N*50],id[N*50];
void init(){tot=u=1;}
void rebuild(){u=1;}
void ins(int k,int c){
if(trans[u][c]){
int v=trans[u][c];
if(len[v]==len[u]+1)u=v,add(1,m,k,root[v]);
else{
int x=++tot;add(1,m,k,root[x]);len[x]=len[u]+1;
memcpy(trans[x],trans[v],sizeof(trans[v]));
fa[x][0]=fa[v][0];fa[v][0]=x;
for(;u&&trans[u][c]==v;u=fa[u][0])trans[u][c]=x;
u=x;
}
}
else{
int x=++tot;add(1,m,k,root[x]);len[x]=len[u]+1;
for(;u&&trans[u][c]==0;u=fa[u][0])trans[u][c]=x;
if(u==0)fa[x][0]=1;
else{
int v=trans[u][c];
if(len[v]==len[u]+1)fa[x][0]=v;
else{
int w=++tot;
len[w]=len[u]+1;
fa[w][0]=fa[v][0];
memcpy(trans[w],trans[v],sizeof(trans[w]));
fa[x][0]=fa[v][0]=w;
for(;u&&trans[u][c]==v;u=fa[u][0])trans[u][c]=w;
}
}
u=x;
}
}
void update(int now){
if(mx[ch[now][0]]>=mx[ch[now][1]])mx[now]=mx[ch[now][0]],id[now]=id[ch[now][0]];
else mx[now]=mx[ch[now][1]],id[now]=id[ch[now][1]];
}
void add(int l,int r,int x,int &now){
if(now==0)now=++cnt;
if(l==r){mx[now]++;id[now]=l;return;}
int mid=(l+r)>>1;
if(x>mid)add(mid+1,r,x,ch[now][1]);
else add(l,mid,x,ch[now][0]);
update(now);
}
data check(int l,int r,int L,int R,int now){
if(l==L&&r==R)return (data(mx[now],id[now]));
int mid=(l+r)>>1;
if(L>mid)return check(mid+1,r,L,R,ch[now][1]);
else if(R<=mid)return check(l,mid,L,R,ch[now][0]);
else return max(check(l,mid,L,mid,ch[now][0]),check(mid+1,r,mid+1,R,ch[now][1]));
}
void merge(int l,int r,int &x,int y){
if(!x||!y){x=x|y;return;}
if(l==r){mx[x]+=mx[y];return;}
int mid=(l+r)>>1;
merge(l,mid,ch[x][0],ch[y][0]);
merge(mid+1,r,ch[x][1],ch[y][1]);
update(x);
}
void dfs(int u){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
dfs(v);
merge(1,m,root[u],root[v]);
}
for(int i=0;i<que[u].size();i++){
data a=check(1,m,que[u][i].a,que[u][i].b,root[u]);
if(a.mx==0)continue;
ans[que[u][i].id]=a;
}
}
void work(){
int now=1;
int lon=0;
for(int i=1;i<=L;i++){
while(trans[now][S[i]-'a'+1]==0&&now)now=fa[now][0],lon=len[now];
if(now==0){
for(int j=0;j<vec[i].size();j++)ans[vec[i][j].id].id=vec[i][j].a;
now=1;lon=0;continue;
}
now=trans[now][S[i]-'a'+1];lon++;
for(int j=0;j<vec[i].size();j++){
ans[vec[i][j].id].id=vec[i][j].a;
if(lon<i-vec[i][j].l+1)continue;
int x=now;
for(int k=20;k>=0;k--)
if(len[fa[x][k]]>=i-vec[i][j].l+1)x=fa[x][k];
qu a=qu(vec[i][j].id,vec[i][j].a,vec[i][j].b);
que[x].push_back(a);
}
}
}
}sam;
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
int main(){
scanf("%s",S+1);
scanf("%d",&m);
sam.init();
for(int i=1;i<=m;i++){
scanf("%s",s+1);
int len=strlen(s+1);
for(int j=1;j<=len;j++)sam.ins(i,s[j]-'a'+1);
sam.rebuild();
}
for(int i=2;i<=sam.tot;i++)add(sam.fa[i][0],i);
for(int j=1;j<=20;j++)
for(int i=2;i<=sam.tot;i++)
sam.fa[i][j]=sam.fa[sam.fa[i][j-1]][j-1];
scanf("%d",&n);
for(int i=1;i<=n;i++){
int c=read(),d=read(),a=read(),b=read();
vec[b].push_back(ques(a,c,d,i));
}
L=strlen(S+1);
sam.work();
sam.dfs(1);
for(int i=1;i<=n;i++)printf("%d %d\n",ans[i].id,ans[i].mx);
return 0;
}

CF666E Forensic Examination(后缀自动机+线段树合并)的更多相关文章

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

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

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

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

  3. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  4. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  5. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  6. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  7. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  8. [CF666E]Forensic Examination:后缀自动机+线段树合并

    分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...

  9. CF666E Forensic Examination [后缀自动机,线段树合并]

    洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...

随机推荐

  1. JS取出特定字符前后的字符串,针对一串字符里面的单个字符前后的字符串

    //针对一串自负里面的单个字符前后的字符串<!doctype html> <html> <head> <meta charset="utf-8&qu ...

  2. laravel报错:Unable to detect application namespace.

    使用报错:Unable to detect application namespace. 是conposer.json格式不对

  3. Hive学习:Hive连接JOIN用例详解

    1 准备数据: 1.1 t_1 01 张三 02 李四 03 王五 04 马六 05 小七 06 二狗 1.2 t_2 01 11 03 33 04 44 06 66 07 77 08 88 1.3 ...

  4. 小学生都能学会的python(生成器)

    小学生都能学会的python(生成器) 1. 生成器 生成器的本质就是迭代器. 生成器由生成器函数来创建或者通过生成器表达式来创建 # def func(): # lst = [] # for i i ...

  5. NOIP2018提高组省一冲奖班模测训练(四)

    NOIP2018提高组省一冲奖班模测训练(四) 这次比赛只AC了第一题,而且花了40多分钟,貌似是A掉第一题里面最晚的 而且还有一个半小时我就放弃了…… 下次即使想不出也要坚持到最后 第二题没思路 第 ...

  6. 关于@SuppressWarnings("unchecked")注解

    解释一: 屏蔽某些编译时的警告信息         在强制类型转换的时候编译器会给出警告        加上程序代码        @SuppressWarnings("unchecked& ...

  7. Webserver管理系列:4、WinPE

    WinPE能够识别NTFS分区,使用WinPE能够备份/还原系统.能够重置用户password. 首先给大家看下怎样用WinPE重置password: 放入WinPE光盘-〉启动后-〉点開始菜单-〉程 ...

  8. hunnu11544:小明的烦恼——找字符串

    Problem description   小明是个非常优秀的同学.他除了特别公正外,他也非常细心,当然老师肯定也知道,这不,老师又有事情找他帮忙了.老师每周都会给他一个字符串A.然后问小明" ...

  9. codevs 3372 选学霸(hash+并查集+多重背包)

    先通过并查集处理出来有多少种不同的集合,每一个集合有多少人.一定要不要忘记了与别的没有联系的独立点. 并查集的时候能够通过hash处理出来每一个数目同样的集合的个数. 这样以人数为权值.个数为限制进行 ...

  10. D3js-绘制地图时出现过小, 设置scale还是无效 的解决方法

    使用d3绘制某个地市的地图时,把scale成非常大但是还是无法达到想要的效果. //---------------------------------------------------------- ...