题目: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. nginx的日志切割

    nginx日志默认情况下统统写入到一个文件中,文件会变的越来越大,非常不方便查看分析.以日期来作为日志的切割是比较好的,通常我们是以每日来做统计的.下面来说说nginx日志切割. 如果我们使用的是yu ...

  2. centos6.6安装hadoop-2.5.0(三、完全分布式安装)

    操作系统:centos6.6(三台服务器) 环境:selinux disabled:iptables off:java 1.8.0_131 安装包:hadoop-2.5.0.tar.gz hadoop ...

  3. 某些浏览器没有canvas.toBlob 方法的解决方案

    var dataURLtoBlob = require('blueimp-canvas-to-blob'); // 80x60px GIF image (color black, base64 dat ...

  4. HDU 6143 17多校8 Killer Names(组合数学)

    题目传送:Killer Names Problem Description > Galen Marek, codenamed Starkiller, was a male Human appre ...

  5. Android直播实现srs流媒体服务器部署

    链接:http://blog.csdn.net/mr_sk/article/details/71086885 在配置文件中加入 pid 参数执行

  6. 用optional取代null

    Java8引入了java.util.Optional<T>,它是一个封装的Optional值的类.变量存在时,Optional类只是对类简单封装.变量不存在时,缺失的值会被建模成一个空的O ...

  7. HTTP Security Header Not Detected未检测到HTTP安全标头

    遇到此安全问题,只需修改Web.config文件. 如: 未处理之前配置代码如下: <httpProtocol> <customHeaders> <add name=&q ...

  8. 何时使用SUM()与SUMX()

    概述 SUM()是一个聚合函数.在应用将影响公式的所有过滤器后,它会将您指定的单个列中的所有值相加.SUM()不知道行的存在(它不能逐行求值) - 它所能做的就是在应用过滤器之后将所有内容添加到它所呈 ...

  9. arm svc

    隐藏exit,ptrace etc. C示例 #include <sys/ptrace.h> #include <stdio.h> int main() { int r; as ...

  10. 利用scrapy下载图片保存到本地

    1.先声明一下,起始位置已经是将所有的图片链接都能到pipelines.py中 2.创建一个类,继承于ImagesPipeline,因此也就需要导入ImagesPipeline from scrapy ...