http://codeforces.com/problemset/problem/666/E (题目链接)

题意

  给出一个主串$S$,$n$个匹配串编号从$1$到$n$。$m$组询问,每次询问主串的一个子串$S[p_l,p_r]$在编号为$[l,r]$的匹配串的哪一个中出现次数最多。

Solution

  首先我们把匹配串相连,中间用分隔符隔开,构建后缀自动机,并且给自动机主链上的节点打上标记,避免之后找后缀的时候重复计算。

  每个节点与其对应$parent$相连,构建$parent$树。由于$parent$树的性质,那么问题就转化为了对于每一个询问求解在$parent$树上对应$S[p_l,p_r]$的节点子树中,编号为$[l,r]$的节点的众数。那么我们现在就要解决两个问题:第一,如何知道对于每一个询问$S[p_l,p_r]$,它在自动机上匹配以后到达的节点位置;第二,如何求解子树中编号为$[l,r]$的节点的众数。

  第一个问题,我们将整个主串$S$在自动机上匹配,那么我们可以得到一个$pos$数组,$pos[i]$表示主串$S$匹配到第$i$位时在自动机上的节点。于是我们就知道了对应子串$S[1,1],S[1,2],S[1,3],S[1,4]······S[1,|S|]$这些子串在自动机上的对应位置。那么对于每一个$S[p_l,p_r]$,我们根据右端点找到$pos[p_r]$,因为长度变短,所以它的实际位置可能是$pos[p_r]$的$parent$树上的祖先,所以我们倍增跳$parent$就可以了。

  第二个问题,考虑线段树合并。按照一定的顺序线段树合并,确保随着每个节点的处理完成,位置在这个节点的询问也会跟随着一起处理掉。我们求出$parent$树的$dfs$序,将询问按照其位置的$dfs$序升序排列,然后在树上一边走一边线段树合并,在树上走的时候按照$dfs$序从大往小的顺序走。同时按照从后往前的顺序处理询问即可。

细节

  这里线段树的询问返回值用的是pair,感觉应该更好处理一些。出现次数为0记得要特判一下。

代码

// codeforces 666E
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#define LL long long
#define inf (1ll<<30)
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std; const int maxn=500010;
int n,m,Q,tot,cnt,rt[maxn],dfn[maxn],fa[maxn][30],id[maxn],Len[maxn],r[maxn];
char s[maxn],st[maxn],ss[maxn];
vector<int> v[maxn];
struct data {int l,r,sl,sr,n,id;}q[maxn],t[maxn];
struct Pair {
int x,y;
friend bool operator < (Pair a,Pair b) {return a.x==b.x ? a.y>b.y : a.x<b.x;}
}ans[maxn];
struct node {
int son[2];Pair sum;
int& operator [] (int x) {return son[x];}
}tr[maxn*40]; namespace SAM {
int last;
int par[maxn<<1],ch[maxn<<1][27],len[maxn<<1],pos[maxn];
void Extend(int c,int x) {
int np=++m,p=last;last=np;
len[np]=len[p]+1;id[np]=x;r[np]=1;
for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;
if (!p) par[np]=1;
else {
int q=ch[p][c];
if (len[q]==len[p]+1) par[np]=q;
else {
int nq=++m;len[nq]=len[p]+1;id[nq]=id[q];
memcpy(ch[nq],ch[q],sizeof(ch[q]));
par[nq]=par[q];
par[np]=par[q]=nq;
for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
}
}
}
void build(char *r) {
int len=strlen(r+1),id=1;
last=m=1;
for (int i=1;i<=len;i++) {
Extend(r[i]-'a',id);
if (r[i]==123) id++;
}
for (int i=2;i<=m;i++) v[par[i]].push_back(i);
}
void match(char *r) {
int p=1,e=strlen(r+1),l=0;
for (int i=1;i<=e;i++) {
while (p>1 && !ch[p][r[i]-'a']) p=par[p],l=len[p];
if (ch[p][r[i]-'a']) pos[i]=p=ch[p][r[i]-'a'],Len[i]=++l;
}
}
void position() {
for (int i=1;i<=Q;i++) {
int p=pos[q[i].sr];
int tmp=q[i].sr-q[i].sl+1;
if (Len[q[i].sr]<tmp) continue;
t[++tot]=q[i];
for (int j=20;j>=0;j--) if (len[fa[p][j]]>=tmp) p=fa[p][j];
t[tot].n=p;
}
}
}
using namespace SAM; namespace Segtree {
int sz;
void insert(int &k,int l,int r,int x) {
if (!k) k=++sz;
if (l==r) {tr[k].sum=(Pair){1,l};return;}
int mid=(l+r)>>1;
if (x<=mid) insert(tr[k][0],l,mid,x);
else insert(tr[k][1],mid+1,r,x);
tr[k].sum=max(tr[tr[k][0]].sum,tr[tr[k][1]].sum);
}
int merge(int x,int y,int l,int r) {
if (!x || !y) return x|y;
int mid=(l+r)>>1;
if (l==r) {tr[x].sum.x+=tr[y].sum.x;return x;}
tr[x][0]=merge(tr[x][0],tr[y][0],l,mid);
tr[x][1]=merge(tr[x][1],tr[y][1],mid+1,r);
tr[x].sum=max(tr[tr[x][0]].sum,tr[tr[x][1]].sum);
return x;
}
Pair query(int k,int s,int t,int l,int r) {
if (s==l && r==t) return tr[k].sum;
int mid=(l+r)>>1;
if (t<=mid) return query(tr[k][0],s,t,l,mid);
else if (s>mid) return query(tr[k][1],s,t,mid+1,r);
else return max(query(tr[k][0],s,mid,l,mid),query(tr[k][1],mid+1,t,mid+1,r));
}
}
using namespace Segtree; namespace Basic {
bool cmp(data a,data b) {return dfn[a.n]<dfn[b.n];}
void dfs(int x) {
dfn[x]=++cnt;
for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
int len=v[x].size();
for (int i=0;i<len;i++) {
fa[v[x][i]][0]=x;
dfs(v[x][i]);
}
}
void work(int x) {
int len=v[x].size();
reverse(v[x].begin(),v[x].end());
for (int i=0;i<len;i++) work(v[x][i]);
if (r[x]) insert(rt[x],1,m,id[x]);
for (int i=0;i<len;i++) rt[x]=merge(rt[x],rt[v[x][i]],1,m);
for (;tot && t[tot].n==x;tot--)
ans[t[tot].id]=query(rt[x],t[tot].l,t[tot].r,1,m);
}
}
using namespace Basic; int main() {
scanf("%s",s+1);
scanf("%d",&n);
int len=0;
for (int i=1;i<=n;i++) {
scanf("%s",ss+1);
int l=strlen(ss+1);
for (int j=1;j<=l;j++) st[++len]=ss[j];
st[++len]=123;
}
build(st);
match(s);
scanf("%d",&Q);
for (int i=1;i<=Q;i++) scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].sl,&q[i].sr),q[i].id=i;
dfs(1);
position();
sort(t+1,t+1+tot,cmp);
work(1);
for (int i=1;i<=Q;i++) if (!ans[q[i].id].x) ans[q[i].id].y=q[i].l;
for (int i=1;i<=Q;i++) printf("%d %d\n",ans[i].y,ans[i].x);
return 0;
}

【codeforces 666E】 Forensic Examination的更多相关文章

  1. Codeforces 666E E - Forensic Examination SA + 莫队 + 线段树

    E - Forensic Examination 我也不知道为什么这个复杂度能过, 而且跑得还挺快, 数据比较水? 在sa上二分出上下界, 然后莫队 + 线段树维护区间众数. #include< ...

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

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

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

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

  4. 【codeforces 415D】Mashmokh and ACM(普通dp)

    [codeforces 415D]Mashmokh and ACM 题意:美丽数列定义:对于数列中的每一个i都满足:arr[i+1]%arr[i]==0 输入n,k(1<=n,k<=200 ...

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

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

  6. 【codeforces 707E】Garlands

    [题目链接]:http://codeforces.com/contest/707/problem/E [题意] 给你一个n*m的方阵; 里面有k个联通块; 这k个联通块,每个连通块里面都是灯; 给你q ...

  7. 【codeforces 707C】Pythagorean Triples

    [题目链接]:http://codeforces.com/contest/707/problem/C [题意] 给你一个数字n; 问你这个数字是不是某个三角形的一条边; 如果是让你输出另外两条边的大小 ...

  8. 【codeforces 709D】Recover the String

    [题目链接]:http://codeforces.com/problemset/problem/709/D [题意] 给你一个序列; 给出01子列和10子列和00子列以及11子列的个数; 然后让你输出 ...

  9. 【codeforces 709B】Checkpoints

    [题目链接]:http://codeforces.com/contest/709/problem/B [题意] 让你从起点开始走过n-1个点(至少n-1个) 问你最少走多远; [题解] 肯定不多走啊; ...

随机推荐

  1. 动画:view从点逐渐变大(放大效果)

    -(void) animationAlert:(UIView *)view { CAKeyframeAnimation *popAnimation = [CAKeyframeAnimation ani ...

  2. python 回溯法 子集树模板 系列 —— 1、8 皇后问题

    问题 8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 分析 为了简化问题,考虑到8个皇后不同行,则每一行放置一个皇后,每一行的 ...

  3. ubuntu 桌面操作系统安装WPS办公软件的方法

    1.打开ubuntu系统自带的firefox软件 2.打开linux.wps.cn,并点击立即下载 3. 点击下载deb安装包 4.进入下载目录,sudo dpkg -i wps-office_10. ...

  4. idea git pull项目到本地时容易出现的问题

    有时候pull到本地,出了各种错误,其实是因为搞来搞去的,容易出问题,所以最好的方法是拿原有打包好的整个稳定能跑的项目环境, 先git add,然后vcs重置head为hard,然后再pull,一般就 ...

  5. Zabbix实战-简易教程--大型分布式监控系统实现Agent批量快速接入

    一.分布式架构 相信使用zabbix的大神都熟悉他的分布式架构,分布式的优势相当明显,分而治之.比如目前我的架构图如下: 那么,对将要接入监控系统的任何一个agent如何快速定位,并进行接入呢?  问 ...

  6. NodeMCU学习(四):与其他设备通信

    TCP连接 TCP是计算机网络中运输层协议,是应用层协议http协议的支撑协议.两台远程主机之间可以通过TCP/UDP协议进行通信并交换信息,前提是,相互通信的两台主机之间必须知道彼此的IP地址和端口 ...

  7. 使用kubeadm安装kubernetes高可用集群

    kubeadm安装kubernetes高可用集群搭建  第一步:首先搭建etcd集群 yum install -y etcd 配置文件 /etc/etcd/etcd.confETCD_NAME=inf ...

  8. hive orc压缩数据异常java.lang.ClassCastException: org.apache.hadoop.io.Text cannot be cast to org.apache.hadoop.hive.ql.io.orc.OrcSerde$OrcSerdeRow

    hive表在创建时候指定存储格式 STORED AS ORC tblproperties ('orc.compress'='SNAPPY'); 当insert数据到表时抛出异常 Caused by: ...

  9. 20135202闫佳歆--week5 系统调用(下)--学习笔记

    此为个人笔记存档 week 5 系统调用(下) 一.给MenuOS增加time和time-asm命令 这里老师示范的时候是已经做好的了: rm menu -rf 强制删除 git clone http ...

  10. 《Linux内核分析》第三周:Linux系统启动过程

    杨舒雯 原创作品转载请注明出处 Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验--使用gdb跟踪调试内 ...