题目: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. 多点触控 TouchAction

    #TouchAction #TouchAction方法是appium自已定义的新方法 # * 短按 (press) * 释放 (release) * 移动到 (moveTo) * 点击 (tap) * ...

  2. L258 技术转让

    We will inform you of the weight, measurements, number of cases, cost of the drawings and other docu ...

  3. JavaWeb:c3p0配置问题java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector

    错误显示 java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector at dbdemo.JdbcUtils.<clinit> ...

  4. $_SERVER['URI']

    WordPress通过301重定向实现非首先域(非www)跳转向本来是一个很简单事情,由于没弄清楚$_SERVER['HTTP_X_REWRITE_URL'] 和$_SERVER['REQUEST_U ...

  5. ubantu 常用命令

    1.显示日期与时间的命令:date 2.显示日历的命令:cal 3.简单好用的计算器:bc 4.热键“命令补全或文件补齐”:Tab 5.热键“中断目前程序”:Ctrl+C 6.热键“键盘输入结束(En ...

  6. reset.css 文件

    body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,b ...

  7. 【Eigen开源库】linux系统如何安装使用Eigen库

    code /* * File : haedPose.cpp * Coder: * Date : 20181126 * Refer: https://www.learnopencv.com/head-p ...

  8. JS书写规范

    1.js代码是由语句组成的,每一条语句以分号结尾: 语句是有关键字,元素符,表达式组成的:2.js代码严格区分大小写3.所有的标点符号都是英文的4.//表示单行注释,/* */表示多行注释

  9. sql ''增删改'' 笔记

    结构语言分类 DDL(数据定义语言)  create  drop  alter   创建删除以及修改数据库,表,存储过程,触发器,索引.... DML(数据操作语言)   insert  delete ...

  10. Oracle中nvl()、instr()、及执行多条sql事务操作

    Oracle的Nvl函数 nvl( ) 函数 从两个表达式返回一个非null 值. 语法 NVL(eExpression1, eExpression2) 参数 eExpression1, eExpre ...