题意:
给定一个串 $S$ 和若干个串 $T_{i}$
每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号.

数据范围: 需要离线

题解:
首先,很常规的对 $T_{1}$ 到 $T_{rmax}$ 的所有字符串构建一个广义后缀自动机.
来一遍线段树合并,合并的权值是每个 $T$ 串出现的次数.
合并完毕后,再广义后缀自动机上的每个点的线段树上都能查到有哪些串能覆盖当前串.

把询问按照右端点排序,将 $S$ 匹配到广义后缀自动机当中,并通过倍增来将每个询问串进行定位..
最后 DFS 一遍算出答案即可.

还是很优美 + 复杂的.

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 2000000
#define N 30
#define sigma 26
#define inf 1000000000 using namespace std; vector <int> G[maxn];
char str[maxn],ss[maxn];
int str_len,m;
int ss_len[maxn];
int fa[maxn][27];
int ans1[maxn],ans2[maxn]; struct Query{ int l,r,ans,pl,pr,id; }arr[maxn];
int cmp(Query a,Query b) { return a.pr<b.pr; } namespace Seg{
struct Segment_Tree{ int l,r,maxv; }node[maxn<<2];
int root[maxn],nodes;
int newnode(){return ++nodes; }
void update(int p,int l,int r,int &o){
if(!o) o=newnode();
if(l==r) { node[o].maxv+=1; return ; }
int mid=(l+r)>>1;
if(p<=mid) update(p,l,mid,node[o].l);
else update(p,mid+1,r,node[o].r);
node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
int o=newnode(),mid=(l+r)>>1;
if(l==r) {
node[o].maxv=node[x].maxv+node[y].maxv;
return o;
}
node[o].l=merge(node[x].l,node[y].l,l,mid);
node[o].r=merge(node[x].r,node[y].r,mid+1,r);
node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv);
return o;
}
int query_max(int l,int r,int L,int R,int o) {
if(!o||l>r||r<L||l>R) return 0;
if(l>=L&&r<=R) return node[o].maxv;
int mid=(l+r)>>1,res=0;
res=max(res,query_max(l,mid,L,R,node[o].l));
res=max(res,query_max(mid+1,r,L,R,node[o].r));
return res;
}
int query_pos(int p,int l,int r,int L,int R,int o){
if(l>r||r<L||l>R||!o||node[o].maxv<p) return inf;
if(l==r) return l;
int mid=(l+r)>>1,a;
if((a=query_pos(p,l,mid,L,R,node[o].l))!=inf) return a;
else return query_pos(p,mid+1,r,L,R,node[o].r);
}
};
namespace SAM{
int f[maxn],dis[maxn],ch[maxn][N],tot,last;
int C[maxn],rk[maxn];
inline void init(){last=++tot; }
void ins(int c){
int p=last,np,nq;
if(ch[p][c]){
int q=ch[p][c];
if(dis[q]==dis[p]+1) last=q;
else
{
nq=++tot,last=nq;
f[nq]=f[q],f[q]=nq,dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q)ch[p][c]=nq,p=f[p];
}
}
else {
np=++tot,last=np,dis[np]=dis[p]+1;
while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
if(!p) f[np]=1;
else {
int q=ch[p][c];
if(dis[q]==dis[p]+1) f[np]=q;
else
{
nq=++tot;
f[nq]=f[q],f[q]=f[np]=nq,dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
}
}
}
}
}; int head[maxn],to[maxn],nex[maxn],edges;
void addedge(int u,int v) { nex[++edges]=head[u],head[u]=edges,to[edges]=v; }
void DFS(int u) {
for(int v=head[u];v;v=nex[v])
{
DFS(to[v]);
Seg::root[u]=Seg::merge(Seg::root[u],Seg::root[to[v]],1,m);
}
for(int i=0;i<G[u].size();++i) {
int cur=G[u][i];
int a=Seg::query_max(1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
ans1[arr[cur].id] = a;
if(!a) { ans2[arr[cur].id]=arr[cur].l; continue; }
int b=Seg::query_pos(a,1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
ans2[arr[cur].id] = (b==inf)?0:b;
}
} int main(){
//setIO("input");
//==============================================================================
scanf("%s",str+1),str_len = strlen(str + 1);
SAM::init();
for(int i=1;i<=str_len;++i) SAM::ins(str[i]-'a');
int i,j,queries;
for(SAM::init(),scanf("%d",&m),i=1;i<=m;++i)
{
scanf("%s",ss+1),ss_len[i] = strlen(ss+1);
for(SAM::last=1,j=1;j<=ss_len[i];++j)
{
SAM::ins(ss[j]-'a');
Seg::update(i,1,m,Seg::root[SAM::last]);
}
}
for(i=2;i<=SAM::tot;++i) addedge(SAM::f[i],i),fa[i][0]=SAM::f[i];
for(i=1;i<sigma;++i)
for(j=2;j<=SAM::tot;++j) fa[j][i]=fa[fa[j][i-1]][i-1];
for(scanf("%d",&queries),i=1;i<=queries;++i)
scanf("%d%d%d%d",&arr[i].l,&arr[i].r,&arr[i].pl,&arr[i].pr),arr[i].id=i;
sort(arr+1,arr+1+queries,cmp);
//=================================================================依次处理每个询问
int cur=1,p=1,q;
for(i=1;i<=queries;++i) {
for(;cur<=arr[i].pr;++cur) {
int c=str[cur]-'a';
while(p && !SAM::ch[p][c]) p=SAM::f[p];
if(!p) p=1;
else p=SAM::ch[p][c];
}
q=p;
if(SAM::dis[p]>=(arr[i].pr-arr[i].pl+1)) {
for(j=24;j>=0;--j) {
if(SAM::dis[fa[p][j]] >= (arr[i].pr-arr[i].pl+1)) p=fa[p][j];
}
G[p].push_back(i);
}
p=q;
}
DFS(1);
for(i=1;i<=queries;++i) printf("%d %d\n",ans2[i],ans1[i]);
return 0;
}

  

CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增的更多相关文章

  1. 【CF666E】Forensic Examination(后缀自动机,线段树合并)

    [CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...

  2. CF666E Forensic Examination(后缀自动机+动态线段树)

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

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

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

  4. 【BZOJ3413】匹配(后缀自动机,线段树合并)

    [BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...

  5. 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并

    [CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...

  6. 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并

    广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...

  7. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

  8. 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

    题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...

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

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

随机推荐

  1. 大数据量.csv文件导入SQLServer数据库

    前几天拿到了一个400多M的.csv文件,在电脑上打开要好长时间,打开后里面的数据都是乱码.因此,做了一个先转码再导入数据库的程序.100多万条的数据转码+导入在本地电脑上花了4分钟,感觉效率还可以. ...

  2. 苹果操作系统名称演变史 新名称macOS

    历史回顾 发布年代 名称 序号 1994-1999 Classic Mac OS 1-9 2001-2011 Mac OS X 10.0-Lion 2012-2015 OS X Mountain Li ...

  3. node——读取文件中的路径问题

    前提 当前的js文件叫_dirnameandfilename.js和hello.txt同在G:\node练习文件夹下 还有一个_dirnameandfilename.js文件在i:/文件夹,但是i:/ ...

  4. 为Electron 安装 vue-devtool等扩展

    相关代码: https://github.com/WozHuang/Barrage-helper/blob/master/src/main/index.dev.js 在SPA逐渐成为构建优秀交互体验应 ...

  5. js正则表达式注册页面表单验证

    可以这样校验 <html> <head> <meta http-equiv="Content-Type" content="text/htm ...

  6. laravel contains 的用法

    最近在学laravel,做一下学习笔记. 1.contains()方法判断集合是否包含给定的项目: ]);var_dump($collection->contains('Desk'));// t ...

  7. B-Tree概念

    记录下学习B-Tree: concept:(m-阶) 1.  根节点 孩子数 ( 2 <= N <= m) 根节点key数([m/2] - 1 <= n <=  m -1) 2 ...

  8. js兼容性——获取当前浏览器窗口的宽高

    通过onresize事件 window.onresize = function () { document.title = client().width + " "+ client ...

  9. 前后端分离开发,跨域访问的apche设置

    1,如何让Apache支持跨域访问呢? 步骤: 修改httpd.conf,windows中对应的目录是:C:\wamp\bin\apache\Apache2.4.4\conf\httpd.conf 把 ...

  10. 搭建ELK日志分析平台(上)—— ELK介绍及搭建 Elasticsearch 分布式集群

    笔记内容:搭建ELK日志分析平台(上)-- ELK介绍及搭建 Elasticsearch 分布式集群笔记日期:2018-03-02 27.1 ELK介绍 27.2 ELK安装准备工作 27.3 安装e ...