题目:http://codeforces.com/contest/666/problem/E

对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问。

还要处理模式串的区间,可以用线段树。给广义自动机的每个节点开一棵线段树存该节点代表的串在各模式串中的出现情况。

线段树合并到叶子时,直接把出现次数相加。这样会改值,所以如果不新建节点的话,父亲用的孩子的节点,父亲又要改值,在孩子上查询的时候就错了。

  可以每次不是 ( !cr || !pr ) 的时候都新建节点。或者把询问离线挂在自动机节点上,准备把该节点的线段树合并给父亲的时候把该节点的询问先查询掉。

定位询问子串可以倍增。先走一遍得到询问串每个前缀最长匹配到哪个节点,查询子串的时候从该子串右端点对应的节点 cr 开始跳 fa 直到 len[ cr ] >= d && len[ fa ] < d (d 是询问子串长度)。跳的过程可以倍增。

注意倍增边界是 K2 而非 K 。注意数组大小 +5 。

需要判断一下询问子串没在自动机里出现。就是右端点对应的节点的匹配长度都 < d (此时没跳 fa ,是该右端点可能的最大匹配长度)。注意是 “匹配长度” 而不是该节点的 len 。

注意若没出现,返回的应是 ( L , 0 ) 。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define pb push_back
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=5e5+,M=1e5+,K=,K2=,M2=M*K2;
int n,m,Q,cnt=,fa[M],len[M],go[M][K+],pre[M][K2+],ps[N],ct[N];//K2+5!!
int c[N],tx[N],rt[M],tot,ls[M2],rs[M2];
char s[N],t[N];
struct Ques{
int l,r,id;
Ques(int l=,int r=,int i=):l(l),r(r),id(i) {}
}q[N];
struct Node{
int mx,sm;
Node(int m=,int s=):mx(m),sm(s) {}
Node operator+ (const Node &b)const
{ if(sm>=b.sm)return *this; return b;}
}vl[M2],ans[N];
vector<Ques> vt[N];
int cz(int p,int w)
{
int q=go[p][w], nq=++cnt; len[nq]=len[p]+;
fa[nq]=fa[q]; fa[q]=nq;
memcpy(go[nq],go[q],sizeof go[q]);
for(;go[p][w]==q;p=fa[p])go[p][w]=nq;
return nq;
}
int ins(int p,int w)
{
if(go[p][w])
{
int q=go[p][w];
if(len[q]==len[p]+)return q;
return cz(p,w);
}
int np=++cnt; len[np]=len[p]+;
for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
if(!p){fa[np]=;return np;}
int q=go[p][w]; if(len[q]==len[p]+)fa[np]=q;
else fa[np]=cz(p,w); return np;
}
void add(int l,int r,int &cr,int p)
{
if(!cr)cr=++tot; if(l==r){vl[cr]=Node(l,); return;}
int mid=l+r>>;
if(p<=mid)add(l,mid,ls[cr],p);
else add(mid+,r,rs[cr],p);
vl[cr]=vl[ls[cr]]+vl[rs[cr]];
}
void mrg(int l,int r,int &cr,int pr)
{
if(!cr||!pr){if(!cr)cr=pr;return;}
if(l==r){vl[cr].sm+=vl[pr].sm; return;}
int mid=l+r>>;
mrg(l,mid,ls[cr],ls[pr]);
mrg(mid+,r,rs[cr],rs[pr]);
vl[cr]=vl[ls[cr]]+vl[rs[cr]];
}
Node qry(int l,int r,int cr,int L,int R)
{
if(!cr)return Node(L,);///!!
if(l>=L&&r<=R)return vl[cr];
int mid=l+r>>; Node ret=Node(L,);//L
if(L<=mid)ret=qry(l,mid,ls[cr],L,R);
if(mid<R)ret=(ret+qry(mid+,r,rs[cr],L,R));
return ret;
}
void Rsort()
{
for(int i=;i<=cnt;i++)tx[len[i]]++;
for(int i=;i<=cnt;i++)tx[i]+=tx[i-];//0 for root
for(int i=;i<=cnt;i++)c[tx[len[i]]--]=i;
}
int main()
{
scanf("%s",s+); n=strlen(s+);
m=rdn();
for(int i=;i<=m;i++)
{
scanf("%s",t+); int d=strlen(t+);
for(int j=,p=;j<=d;j++)
p=ins(p,t[j]-'a'+), add(,m,rt[p],i);
}
Rsort();
for(int i=;i<=cnt;i++)
{
int cr=c[i];
pre[cr][]=fa[cr];
for(int t=,d=fa[cr];(d=pre[d][t-]);t++)
pre[cr][t]=d;
}
Q=rdn(); int cr=;
for(int i=,c2=;i<=n;i++)
{
int w=s[i]-'a'+;
while(cr&&!go[cr][w])cr=fa[cr],c2=len[cr];
if(!go[cr][w])cr=, c2=;
else cr=go[cr][w], c2++;
ps[i]=cr;ct[i]=c2;
}
for(int i=,l,r,ql,qr;i<=Q;i++)
{
l=rdn();r=rdn();ql=rdn();qr=rdn();
int cr=ps[qr],c2=ct[qr],d=qr-ql+;
if(c2<d){ans[i]=Node(l,);continue;}//l not 1
for(int t=K2;t>=;t--)//K2!!
if(len[pre[cr][t]]>=d) cr=pre[cr][t];
vt[cr].pb(Ques(l,r,i));
}
for(int i=cnt;i>;i--)
{
int cr=c[i];
for(int j=,lm=vt[cr].size();j<lm;j++)
{
Ques q=vt[cr][j];
ans[q.id]=qry(,m,rt[cr],q.l,q.r);
}
mrg(,m,rt[fa[cr]],rt[cr]);
}
for(int i=;i<=Q;i++)printf("%d %d\n",ans[i].mx,ans[i].sm);
return ;
}

CF 666E Forensic Examination——广义后缀自动机+线段树合并的更多相关文章

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

    题目链接 \(Description\) 给定串\(S\)和\(m\)个串\(T_i\).\(Q\)次询问,每次询问\(l,r,p_l,p_r\),求\(S[p_l\sim p_r]\)在\(T_l\ ...

  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. CF 666E Forensic Examination 【SAM 倍增 线段树合并】

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

  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 or SA+线段树合并

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

  9. CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线

    传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...

随机推荐

  1. synchronized(四)

    package com.bjsxt.base.sync005;/** * synchronized的重入 * @author alienware * */public class SyncDubbo1 ...

  2. 解决At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs

    在写spring security小程序时遇到  At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug l ...

  3. mssql,mysql,Oracle 数据库中获得UUID字符串

    sql server : select replace(newId(),'-','') oracle :select sys_guid() from dual SQL> select sys_g ...

  4. c++ 编译报错汇总(随时更新)

    1.invalid new-expression of abstract class type ‘×××ב 这个报错代表一个尝试在实例化一个抽象类,也就是说父类的接口中有纯虚函数在子类中没有实现: ...

  5. Server SSL certificate verification failed: certificate has expired, issuer is not trusted

    Unable to connect to a repository at URL 'https://xxxxx/svn/include' Server SSL certificate verifica ...

  6. 优先队列(挑程)poj 2431

    每次写poj的题都很崩溃,貌似从来没有一次一发就ac的,每次都有特别多的细节需要考虑.还有就是自己写的太粗糙了,应该把每种情况都想到的,总是急着交,然后刷一页wa. 优先队列直接用stl就可以,简单实 ...

  7. jquery选择后代以及toggle,toggleClass用法

    $("#id").find(".classname")      选择id为xx下的类名为xx的元素 toggle  $(this).next(".c ...

  8. ACM/ICPC 2018亚洲区预选赛北京赛站网络赛-B:Tomb Raider(二进制枚举)

    时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 Lara Croft, the fiercely independent daughter of a missing adv ...

  9. C++学习(九)(C语言部分)之 项目 推箱子游戏

    游戏制作 推箱子 步骤分析 1.模板 2.模板分析 组成元素: 空地 墙 人 目的地 箱子 背景 3.如何操作 通过WASD键盘操作人,推着箱子,到达目的地,游戏结束,如果箱子卡在死角则游戏失败 4. ...

  10. 【HDOJ3018】【一笔画问题】【欧拉回路+并查集】

    http://acm.hdu.edu.cn/showproblem.php?pid=3018 Ant Trip Time Limit: 2000/1000 MS (Java/Others)    Me ...