【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并
【CF666E】Forensic Examination
题意:给你一个字符串s和一个字符串集合$\{t_i\}$。有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l...t_r$中的哪个字符串中出现的次数最多,以及最多次数是多少。
$|s|\le 5\times 10^5,\sum |t_i|\le 5\times 10^4,q\le 5\times10^5$
题解:我们对于$t_i$建立广义后缀自动机,并对于每个节点都维护:在它的right集合中,每个字符串出现的次数,以及区间出现次数最大值。这个用线段树合并很容易搞定。我们将询问离线并按右端点排序,对于询问$p_l,p_r$,我们已经将s的前$p_r$位放到广义后缀自动机里匹配得到了其对应的位置,现在我们只需不断沿着pre指针向上跳,找到最后一个长度$\ge p_r-p_l+1$的状态即可。这一步可以用倍增实现。既然我们已经找到了那个状态,我们直接在那个状态的线段树中查询一下$[l,r]$的最大值即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=100010;
const int maxm=500010;
int ch[maxn][26],pre[maxn],mx[maxn];
int n,m,tot,N,Q,cnt,last;
char S[maxm],T[maxn];
int to[maxn],nxt[maxn],head[maxn],rt[maxn],fa[18][maxn],Log[maxn];
int sl[maxm],sr[maxm],pl[maxm],pr[maxm];
vector<int> q1[maxm],q2[maxn];
vector<int>::iterator it;
struct node
{
int x,y;
node() {}
node(int a,int b) {x=a,y=b;}
bool operator < (const node &a) const {return (x==a.x)?(y>a.y):(x<a.x);}
}ans[maxm];
struct sag
{
int ls,rs;
node v;
}s[maxn*20];
void extend(int x)
{
int p=last;
if(ch[p][x])
{
int q=ch[p][x];
if(mx[q]==mx[p]+1) last=q;
else
{
int nq=++tot; last=nq;
mx[nq]=mx[p]+1,pre[nq]=pre[q],pre[q]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
}
}
else
{
int np=++tot;
last=np,mx[np]=mx[p]+1;
for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np;
if(!p) pre[np]=1;
else
{
int q=ch[p][x];
if(mx[q]==mx[p]+1) pre[np]=q;
else
{
int nq=++tot;
mx[nq]=mx[p]+1,pre[nq]=pre[q],pre[q]=pre[np]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
}
}
}
}
inline void add(int a,int b)
{
to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
void insert(int l,int r,int &x,int a)
{
if(!x) x=++N;
if(l==r)
{
s[x].v.x++,s[x].v.y=l;
return ;
}
int mid=(l+r)>>1;
if(a<=mid) insert(l,mid,s[x].ls,a);
else insert(mid+1,r,s[x].rs,a);
s[x].v=max(s[s[x].ls].v,s[s[x].rs].v);
}
int merge(int x,int y)
{
if(!x||!y) return x^y;
if(!s[x].ls&&!s[x].rs)
{
s[x].v.x+=s[y].v.x;
return x;
}
s[x].ls=merge(s[x].ls,s[y].ls),s[x].rs=merge(s[x].rs,s[y].rs);
s[x].v=max(s[s[x].ls].v,s[s[x].rs].v);
return x;
}
node query(int l,int r,int x,int a,int b)
{
if(a<=l&&r<=b) return s[x].v;
int mid=(l+r)>>1;
if(b<=mid) return query(l,mid,s[x].ls,a,b);
if(a>mid) return query(mid+1,r,s[x].rs,a,b);
return max(query(l,mid,s[x].ls,a,b),query(mid+1,r,s[x].rs,a,b));
}
void dfs(int x)
{
for(int i=head[x];i!=-1;i=nxt[i])
{
dfs(to[i]),rt[x]=merge(rt[x],rt[to[i]]);
}
for(it=q2[x].begin();it!=q2[x].end();it++)
{
int a=*it;
ans[a]=query(1,m,rt[x],pl[a],pr[a]);
}
}
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
int main()
{
scanf("%s",S),n=strlen(S);
m=rd();
int i,j,a,b,u,dep;
tot=1;
for(i=1;i<=m;i++)
{
scanf("%s",T),a=strlen(T);
for(last=1,j=0;j<a;j++) extend(T[j]-'a'),insert(1,m,rt[last],i);
}
Q=rd();
for(i=1;i<=Q;i++)
{
pl[i]=rd(),pr[i]=rd(),sl[i]=rd()-1,sr[i]=rd()-1,q1[sr[i]].push_back(i);
}
memset(head,-1,sizeof(head));
for(i=2;i<=tot;i++) add(pre[i],i),fa[0][i]=pre[i],Log[i]=Log[i>>1]+1;
for(j=1;(1<<j)<=tot;j++) for(i=1;i<=tot;i++) fa[j][i]=fa[j-1][fa[j-1][i]];
for(u=1,dep=0,i=0;i<n;i++)
{
while(u&&!ch[u][S[i]-'a']) u=pre[u],dep=mx[u];
if(!u)
{
u=1,dep=0;
continue;
}
u=ch[u][S[i]-'a'],dep++;
for(it=q1[i].begin();it!=q1[i].end();it++)
{
a=*it,b=u;
if(dep<sr[a]-sl[a]+1) continue;
for(j=Log[mx[u]+1];j>=0;j--) if(mx[fa[j][b]]>=sr[a]-sl[a]+1) b=fa[j][b];
q2[b].push_back(a);
}
}
dfs(1);
for(i=1;i<=Q;i++)
{
if(!ans[i].x) ans[i].y=pl[i];
printf("%d %d\n",ans[i].y,ans[i].x);
}
return 0;
}//popo 2 poq popoqqq 2 3 4
【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并的更多相关文章
- CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增
题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ...
- 【CF666E】Forensic Examination(后缀自动机,线段树合并)
[CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...
- CF666E Forensic Examination(后缀自动机+动态线段树)
题意 给你一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l..p_r]$ 在 $T[l..r]$ 中的哪个串里的出现次数最多,并输出出现 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
- BZOJ4556 Tjoi2016&Heoi2016 字符串【后缀自动机+倍增+线段树合并】
Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开 ...
- 【BZOJ3413】匹配(后缀自动机,线段树合并)
[BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并
广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...
随机推荐
- Asp.Net HttpPostedFile和base64两种上传图片(文件)方式
之前上传图片基本都是用的HttpPostedFile方式获取图片,这次因为需求关系,要对准备上传的图片进行删除,最后提交的时候才去保存图片到服务器, 找了下资料,html5有个新的东西出来,js 里面 ...
- Mac 10.12下安装python3环境
python3感觉用虚拟环境会比较好操作一些,也不用直接卸载python2. 一.基于brew快速安装 # 安装python3 brew install python3 # 安装pip(好像3自带pi ...
- Android Studio 安装与设置
http://www.cnblogs.com/abao0/p/6934127.html
- python测试开发django-51.Ajax发送post请求登录案例
前言 我想实现一个登录功能:登录的接口是另外一个地方提供,页面上点登录按钮的时候,先访问登录接口,根据接口返回json信息判断是否登录成功,登录成功页面跳转,登录不成功,在登录首页显示失败原因 登录页 ...
- CSS魔法堂:稍稍深入伪类选择器
前言 过去零零星星地了解和使用:link.::after和content等伪类.伪元素选择器,最近看书时发现这方面有所欠缺,于是决定稍微深入学习一下,以下为伪类部分的整理. 伪类 伪类选择器实质上 ...
- mssql f_Split
mssql可以如下CREATE FUNCTION [dbo].[f_Split] ( @val varchar(max),@Splits varchar(100))RETURNS @Table TAB ...
- EChart配置
EChart配置 https://www.jianshu.com/p/3cf80b96a65d
- ACC自适应巡航控制系统介绍
本文是关于ACC自适应巡航控制系统的介绍,罗孚从个人视角出发,描述对ACC系统的理解,以及在一些使用场景下的思考. 什么是ACC? ACC系统是在定速巡航装置的基础上发展而来的,区别在于定速巡航只能限 ...
- 160多个android开源码汇总
第一部分 个性化控件(View) 主要介绍那些不错个性化的View,包含ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.Pro ...
- zigw 和 nanoWatch, libudev.so 和 XMR 挖矿程序查杀记录
最近这两天以来,服务器一致声音很响.本来以为有同事在运行大的程序,结果后来发现持续很长时间都是这样,并没有停的样子.后来查了一下,发现有几个可疑进程导致,干掉之后,果然服务器静悄悄了. 但是,问题并没 ...