题目链接

\(Description\)

给定串\(S\)和\(m\)个串\(T_i\)。\(Q\)次询问,每次询问\(l,r,p_l,p_r\),求\(S[p_l\sim p_r]\)在\(T_l\sim T_r\)中的哪个串出现次数最多,输出最多次数以及它是\(T\)中的第几个。若最多的有多个,输出下标最小的。

\(Solution\)

挺好的题吧

对\(T\)个串建SAM,然后要求出SAM每个节点上\(|right|\)最大的是哪个串。

每个节点的\(|right|\)可以在DFS parent树时合并子节点得到。如果用线段树,区间\(|right|\)最大的是哪个串也可以维护出来。

那么可以离线,在每个点处处理该点上的询问,边DFS边合并线段树得到所有答案。(当然可持久化一下在线也行?)

怎么得到\(S[p_l\sim p_r]\)在SAM上的匹配节点呢?

维护一个节点指针\(p\),拿\(S\)在SAM上尽可能匹配,匹配不了就跳\(fa\)。这样能保证当前\(S\)的后缀(\(S[i]\))一定在\(p\)节点出现了。

所以在\(i\)这里处理\(p_r=i\)的询问。只要我们从当前节点\(p\)一直跳\(fa\),就能找到\(S[p_l,p_r]\)所在的节点,其答案就是该节点的\(|right|\)状态。可以用倍增实现。

(就是从\(S[1,p_r]\)所匹配的节点\(p\)往上跳,跳到从上往下第一个\(len_x\geq p_r-p_l+1\)的节点\(x\),\(x\)就是\(S[p_l,p_r]\)所在的节点)

如果出现次数为\(0\)的话也要输出最靠前的(即\(l\))。=-=

唉 6点多写完代码调到现在 心累

//529ms	56700KB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define Bit 16
const int N=5e5+5,M=5e4+5,S=M<<1; int m,root[S],fa[S][18];
char s[N],tmp[M];
struct Edge
{
int Enum,H[N],nxt[N],to[N];
inline void AddEdge(int u,int v){
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
}Pos,Qy;
struct Edge2
{
int Enum,H[S],nxt[S],to[S];
inline void AddEdge(int u,int v){
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
}Par;
struct Queries{
int l,r,pl,pr;
}q[N];
struct Suffix_Automaton
{
int tot,las,fa[S],son[S][26],len[S]; Suffix_Automaton() {tot=las=1;}
void Insert(int c)
{
int np=++tot,p=las; len[las=np]=len[p]+1;
for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=son[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot; len[nq]=len[p]+1;
memcpy(son[nq],son[q],sizeof son[q]);
fa[nq]=fa[q], fa[q]=fa[np]=nq;
for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
}
}
}
}sam;
struct Node
{
int val,id;
bool operator <(const Node &x)const{
return val<x.val||(val==x.val&&id>x.id);
}
}Ans[N];
struct Segment_Tree
{
#define S M*17
#define lson son[x][0]
#define rson son[x][1]
int tot,son[S][2];
Node node[S];
#undef S
#define Update(x) node[x]=std::max(node[lson],node[rson]);
void Insert(int &x,int l,int r,int pos)
{
x=++tot;
if(l==r) return (void)(node[x]=(Node){1,pos});
int m=l+r>>1;
pos<=m ? Insert(lson,l,m,pos) : Insert(rson,m+1,r,pos);
Update(x);//Update
}
int Merge(int x,int y)
{
if(!x||!y) return x^y;
if(!lson&&!rson) return node[x].val+=node[y].val, x;//叶节点,合并right
lson=Merge(lson,son[y][0]), rson=Merge(rson,son[y][1]);
Update(x); return x;
}
Node Query(int x,int l,int r,int L,int R)
{
if(!x) return (Node){0,L};
if(L<=l && r<=R) return node[x];
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query(lson,l,m,L,R),Query(rson,m+1,r,L,R));
else return Query(lson,l,m,L,R);
return Query(rson,m+1,r,L,R);
}
}T; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void DFS(int x)
{
for(int i=Par.H[x]; i; i=Par.nxt[i])
DFS(Par.to[i]), root[x]=T.Merge(root[x],root[Par.to[i]]);
for(int i=Pos.H[x],id; i; i=Pos.nxt[i])
id=Pos.to[i], Ans[id]=T.Query(root[x],1,m,q[id].l,q[id].r);
} int main()
{
scanf("%s",s+1); int n=strlen(s+1);
m=read();
for(int i=1; i<=m; ++i)
{
scanf("%s",tmp), sam.las=1;
for(int j=0,l=strlen(tmp); j<l; ++j)
sam.Insert(tmp[j]-'a'), T.Insert(root[sam.las],1,m,i);//不就是每位的|right[las]|=1吗→_→我在纠结什么
//就是las节点的线段树上,i位置的|right|=1,给i位置+1
}
int Q=read();
for(int i=1; i<=Q; ++i)
q[i]=(Queries){read(),read(),read(),read()}, Qy.AddEdge(q[i].pr,i);
int lim=sam.tot;
for(int x=2; x<=lim; ++x) Par.AddEdge(fa[x][0]=sam.fa[x],x);
for(int i=1; i<=Bit; ++i)
for(int x=2; x<=lim; ++x)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int c,now=0,p=1,i=1; i<=n; ++i)
{
if(sam.son[p][c=s[i]-'a']) p=sam.son[p][c], ++now;//!!!靠 这写错调了两个小时 唉 心累
else
{
for(c=s[i]-'a'; p&&!sam.son[p][c]; p=sam.fa[p]);
if(!p) {p=1, now=0; continue;}
now=sam.len[p]+1, p=sam.son[p][c];
}
for(int j=Qy.H[i],len,id; j; j=Qy.nxt[j])
{
id=Qy.to[j];
if(now<(len=q[id].pr-q[id].pl+1)) continue;
int x=p;
for(int i=Bit; ~i; --i)
if(sam.len[fa[x][i]]>=len) x=fa[x][i];
Pos.AddEdge(x,id);
}
}
DFS(1);
for(int i=1; i<=Q; ++i)
if(!Ans[i].val) printf("%d 0\n",q[i].l);
else printf("%d %d\n",Ans[i].id,Ans[i].val); return 0;
}

Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)的更多相关文章

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

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

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

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

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

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

  4. Codeforces 666E Forensic Examination SAM or SA+线段树合并

    E. Forensic Examination http://codeforces.com/problemset/problem/666/E 题目大意:给模式串S以及m个特殊串,q个询问,询问S的子串 ...

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

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

  6. CF666E Forensic Examination(后缀自动机+线段树合并)

    给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串 ...

  7. Codeforces 666E Forensic Examination(广义后缀自动机+线段树合并)

    将所有串(包括S)放一块建SAM.对于询问,倍增定位出该子串所在节点,然后要查询的就是该子串在区间内的哪个字符串出现最多.可以线段树合并求出该节点在每个字符串中的出现次数. #include<b ...

  8. Codeforces 666E Forensic Examination SAM+权值线段树

    第一次做这种$SAM$带权值线段树合并的题 然而$zjq$神犇看完题一顿狂码就做出来了 $Orz$ 首先把所有串当成一个串建$SAM$ 我们对$SAM$上每个点 建一棵权值线段树 每个叶子节点表示一个 ...

  9. CF 666E Forensic Examination 【SAM 倍增 线段树合并】

    CF 666E Forensic Examination 题意: 给出一个串\(s\)和\(n\)个串\(t_i\),\(q\)次询问,每次询问串\(s\)的子串\(s[p_l:p_r]\)在串\(t ...

随机推荐

  1. 统计nginx日志里访问次数最多的前十个IP

    awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr -k1 | head -n 10

  2. 创建WIFI热点

    @echo off:beginecho 笔记本做无线WiFi程序(首次使用请先设置WiFi帐户.)echo 1.设置WiFi帐户,请按1echo 2.开启WiFi功能,请按2echo 3.闭关WiFi ...

  3. 对象存储服务(Object Storage Service,简称 OSS)

    阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量.安全.低成本.高可靠的云存储服务.它具有与平台无关的RESTful API接口,能够提供99.99 ...

  4. jetbrains全系列可用例:IDEA、WebStorm、phpstorm、clion等激活到2099

    破解补丁激活 之前看了好多的其它的方法感觉都不是很靠谱还是这个本人亲试可以长期有效不仅能激活pycharm.jetbrains全系列可用例:IDEA.WebStorm.phpstorm.clion等激 ...

  5. 发布WebApi项目时,提示未包含bin\yourDocumentationFile.xml文档文件

    Open your publishprofile (*.pubxml) and include this code into "Project" element: <Item ...

  6. rabbitmq3.7.5 centos7 安装笔记

    先安装各种依赖文件: yum -y install gcc glibc-devel make ncurses-devel openssl-devel xmlto perl wget vim 1. ra ...

  7. ionic2/cordova自定义插件集成aar包

    一.准备自定义插件 1. 准备:安装plugman npm install -g plugman 2. 新建组件 plugman create --name MyPlugin --plugin_id ...

  8. admin密码对应的MD5值

    admin密码对应的MD5值,16位和32位 admin密码对应的MD5值,16位和32位 admin的md5值是多少,常用密码加密md5值,123456,admin,admin888 如果遇到MD5 ...

  9. pip安装其他包报错

    pip安装时报错  Unknown or unsupported command 'install 一.是否配置了路径 配置了看下面的方法. 二.有多个pip系统不知道调用哪个. 1.where pi ...

  10. Codeforces 915G Coprime Arrays 莫比乌斯反演 (看题解)

    Coprime Arrays 啊,我感觉我更本不会莫比乌斯啊啊啊, 感觉每次都学不会, 我好菜啊. #include<bits/stdc++.h> #define LL long long ...