思路

线段树合并+广义SAM

先把所有串都插入SAM中,然后用线段树合并维护right集合,对S匹配的同时离线询问,然后就好啦

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 100100;
const int MAXlog = 20;
char s[500500],t[50500];
int lens,m,q;
namespace Segment_Tree{
int Nodecnt=0,root[100100];
struct Node{
int lson,rson,maxx,maxnum;
}Seg[100100*50];
void pushup(int o){
if(Seg[Seg[o].lson].maxx>Seg[Seg[o].rson].maxx){
Seg[o].maxx=Seg[Seg[o].lson].maxx;
Seg[o].maxnum=Seg[Seg[o].lson].maxnum;
}
else if(Seg[Seg[o].rson].maxx>Seg[Seg[o].lson].maxx){
Seg[o].maxx=Seg[Seg[o].rson].maxx;
Seg[o].maxnum=Seg[Seg[o].rson].maxnum;
}
else if(Seg[Seg[o].rson].maxx==Seg[Seg[o].lson].maxx){
if(Seg[Seg[o].lson].maxnum<Seg[Seg[o].rson].maxnum){
Seg[o].maxx=Seg[Seg[o].lson].maxx;
Seg[o].maxnum=Seg[Seg[o].lson].maxnum;
}
else{
Seg[o].maxx=Seg[Seg[o].rson].maxx;
Seg[o].maxnum=Seg[Seg[o].rson].maxnum;
}
}
}
void merge(int l,int r,int &lroot,int rroot){// rroot -> lroot
if(lroot*rroot==0){
lroot=lroot+rroot;
return;
}
if(l==r){
Seg[lroot].maxx+=Seg[rroot].maxx;
Seg[lroot].maxnum=l;
return;
}
int mid=(l+r)>>1;
merge(l,mid,Seg[lroot].lson,Seg[rroot].lson);
merge(mid+1,r,Seg[lroot].rson,Seg[rroot].rson);
pushup(lroot);
}
void add(int l,int r,int pos,int &o){
if(!o)
o=++Nodecnt;
if(l==r){
Seg[o].maxx++;
Seg[o].maxnum=pos;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)
add(l,mid,pos,Seg[o].lson);
else
add(mid+1,r,pos,Seg[o].rson);
pushup(o);
}
Node query(int L,int R,int l,int r,int o){
if(!o)
return (Node){0,0,0,L};
if(L<=l&&r<=R)
return Seg[o];
int mid=(l+r)>>1;
if(R<=mid)
return query(L,R,l,mid,Seg[o].lson);
else{
if(L>mid)
return query(L,R,mid+1,r,Seg[o].rson);
else{
Node ans,lx=query(L,R,l,mid,Seg[o].lson),rx=query(L,R,mid+1,r,Seg[o].rson);
if(lx.maxx>rx.maxx){
ans.maxx=lx.maxx;
ans.maxnum=lx.maxnum;
}
else if(rx.maxx>lx.maxx){
ans.maxx=rx.maxx;
ans.maxnum=rx.maxnum;
}
else{
if(lx.maxnum<rx.maxnum){
ans.maxx=lx.maxx;
ans.maxnum=lx.maxnum;
}
else{
ans.maxx=rx.maxx;
ans.maxnum=rx.maxnum;
}
}
return ans;
}
}
}
}
namespace SAM{
int Nodecnt=1,trans[MAXN][26],maxlen[MAXN],suflink[MAXN];
int New_state(int _maxlen,int *_trans,int _suflink){
++Nodecnt;
maxlen[Nodecnt]=_maxlen;
if(_trans)
for(int i=0;i<26;i++)
trans[Nodecnt][i]=_trans[i];
suflink[Nodecnt]=_suflink;
return Nodecnt;
}
int add_len(int u,int c){
if(trans[u][c]){
int v=trans[u][c];
if(maxlen[v]==maxlen[u]+1)
return v;
int y=New_state(maxlen[u]+1,trans[v],suflink[v]);
suflink[v]=y;
while(u&&trans[u][c]==v){
trans[u][c]=y;
u=suflink[u];
}
return y;
}
else{
int z=New_state(maxlen[u]+1,NULL,0);
while(u&&trans[u][c]==0){
trans[u][c]=z;
u=suflink[u];
}
if(!u){
suflink[z]=1;
return z;
}
int v=trans[u][c];
if(maxlen[v]==maxlen[u]+1){
suflink[z]=v;
return z;
}
int y=New_state(maxlen[u]+1,trans[v],suflink[v]);
suflink[v]=suflink[z]=y;
while(u&&trans[u][c]==v){
trans[u][c]=y;
u=suflink[u];
}
return z;
}
}
void insert(char *s,int len,int inq){
int last=1;
for(int i=1;i<=len;i++){
last=add_len(last,s[i]-'a');
Segment_Tree::add(1,m,inq,Segment_Tree::root[last]);
// printf("o=%d\n",last);
}
}
void debug(void){
for(int i=1;i<=Nodecnt;i++){
printf("%d: maxlen=%d suflink=%d\n",i,maxlen[i],suflink[i]);
}
}
}
namespace parent_tree{
int fir[100100],v[100100],nxt[100100],cnt,fa[100100][MAXlog];
void addedge(int ui,int vi){
++cnt;
v[cnt]=vi;
nxt[cnt]=fir[ui];
fir[ui]=cnt;
}
void build(void){
for(int i=2;i<=SAM::Nodecnt;i++){
addedge(SAM::suflink[i],i);
fa[i][0]=SAM::suflink[i];
}
}
void pre(void){
build();
for(int i=1;i<MAXlog;i++){
for(int j=1;j<=SAM::Nodecnt;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
void debug(void){
for(int i=0;i<16;i++){
for(int j=1;j<=SAM::Nodecnt;j++)
printf("fa[%d][%d]=%d\n",j,i,fa[j][i]);
}
}
}
struct Ask{
int l,r,pl,pr,which,times;
}Q[500500];
struct table{
int fir[500500],v[500500],nxt[500500],cnt;
void add(int u,int num){
++cnt;
v[cnt]=num;
nxt[cnt]=fir[u];
fir[u]=cnt;
}
}Qr,QN;
void dfs(int u){
for(int i=parent_tree::fir[u];i;i=parent_tree::nxt[i]){
dfs(parent_tree::v[i]);
Segment_Tree::merge(1,m,Segment_Tree::root[u],Segment_Tree::root[parent_tree::v[i]]);
}
for(int i=QN.fir[u];i;i=QN.nxt[i]){
int o=QN.v[i];
// printf("id=%d\n",o);
Segment_Tree::Node tmp=Segment_Tree::query(Q[o].l,Q[o].r,1,m,Segment_Tree::root[u]);
Q[o].which=tmp.maxnum;
Q[o].times=tmp.maxx;
// printf("which=%d time=%d\n",Q[o].which,Q[o].times);
}
}
int main(){
// freopen("test.in","r",stdin);
// freopen("test.out","w",stdout);
scanf("%s",s+1);
lens=strlen(s+1);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%s",t+1);
SAM::insert(t,strlen(t+1),i);
}
// SAM::debug();
parent_tree::pre();
// parent_tree::debug();
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d %d %d %d",&Q[i].l,&Q[i].r,&Q[i].pl,&Q[i].pr);
Qr.add(Q[i].pr,i);
}
int nowlen=0,nowp=1;
for(int i=0;i<lens;i++){
// printf("i=%d nowp=%d len=%d\n",i+1,nowp,nowlen);
if(SAM::trans[nowp][s[i+1]-'a']){
nowp=SAM::trans[nowp][s[i+1]-'a'];
nowlen++;
}
else{
while(nowp&&(SAM::trans[nowp][s[i+1]-'a']==0))
nowp=SAM::suflink[nowp];
if(!nowp){
nowp=1;
nowlen=0;
continue;
}
nowlen=SAM::maxlen[nowp]+1;
nowp=SAM::trans[nowp][s[i+1]-'a'];
}
for(int j=Qr.fir[i+1];j;j=Qr.nxt[j]){
// printf("id=%d\n",Qr.v[j]);
int lentmp=Q[Qr.v[j]].pr-Q[Qr.v[j]].pl+1;
if(nowlen<lentmp)
continue;
int tmpp=nowp;
for(int k=MAXlog-1;k>=0;k--)
if(SAM::maxlen[parent_tree::fa[tmpp][k]]>=lentmp)
tmpp=parent_tree::fa[tmpp][k];
QN.add(tmpp,Qr.v[j]);
// printf("getNode=%d\n",tmpp);
}
}
dfs(1);
for(int i=1;i<=q;i++){
if(Q[i].times)
printf("%d %d\n",Q[i].which,Q[i].times);
else
printf("%d %d\n",Q[i].l,Q[i].times);
}
return 0;
}

CF666E Forensic Examination的更多相关文章

  1. CF666E Forensic Examination SAM+倍增,线段树和并

    题面: 给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数.如有多解输出最靠前的那一个. 分析: 第 ...

  2. CF666E Forensic Examination [后缀自动机,线段树合并]

    洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...

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

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

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

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

  5. CF666E Forensic Examination(后缀自动机+动态线段树)

    题意 给你一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l..p_r]$ 在 $T[l..r]$ 中的哪个串里的出现次数最多,并输出出现 ...

  6. CF666E Forensic Examination SAM+线段树合并+前缀树倍增

    $ \color{#0066ff}{ 题目描述 }$ 给你一个串\(S\)以及一个字符串数组\(T[1..m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l..p_r]\)在\(T[l. ...

  7. CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增

    题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ...

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

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

  9. CF666E Forensic Examination——SAM+线段树合并+倍增

    RemoteJudge 题目大意 给你一个串\(S\)以及一个字符串数组\(T[1...m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l...p_r]\)在\(T[l...r]\)中的 ...

随机推荐

  1. Prometheus部署监控容器

    Prometheus架构描述 Prometheus 是一个非常优秀的监控工具.准确的说,应该是监控方案.Prometheus 提供了监控数据搜集.存储.处理.可视化和告警一套完整的解决方案 Prome ...

  2. [ROS]激光驱动安装

    参考资料: https://blog.csdn.net/hongliang2009/article/details/73302986 https://blog.csdn.net/bohaijun_12 ...

  3. 2019春第五周作业Compile Summarize

    这个作业属于哪个课程 C语言程序设计II 这个作业要求在哪里 在这里 我在这个课程的目标是 能够精通关于数组内部运作原理 这个作业在哪个具体方面帮助我实现目标 如何输出一行的连续字符 参考文献与网址 ...

  4. MPLS的模拟学习过程

    1.场景拓扑 使用小凡模拟器搭建了如下网络拓扑,使用的镜像为:c3640-jk9o3s-mz.122-15.T9.bin 相关的配置在下方 如果重复实验,需要清空设备的配置,知道路由器的密码,操作步骤 ...

  5. java生成二维码的几个方法

    1: 使用SwetakeQRCode在Java项目中生成二维码 http://swetake.com/qr/ 下载地址 或着http://sourceforge.jp/projects/qrcode/ ...

  6. 使用Apache JMeter对SQL Server、Mysql、Oracle压力测试(二)

    接着第一篇的写: 第三步,测试SQL Server数据库的性能: a.加载JDBC SQL Server驱动.添加线程组和设置线程属性和第二步一样,就不再赘述了: b.设置JDBC Connectio ...

  7. python的高级数组之稀疏矩阵

    稀疏矩阵的定义: 具有少量非零项的矩阵(在矩阵中,若数值0的元素数目远多于非0元素的数目,并且非0元素分布没有规律时,)则称该矩阵为稀疏矩阵:相反,为稠密矩阵.非零元素的总数比上矩阵所有元素的总数为矩 ...

  8. 使用 notify.js 桌面提醒

    //var iN = new iNotify({ // effect: 'flash', // interval: 500, // message: "有消息拉!", // aud ...

  9. G面经Prepare: Print Zigzag Matrix

    For instance, give row = 4, col = 5, print matrix in zigzag order like: [1, 8, 9, 16, 17] [2, 7, 10, ...

  10. 我的第三篇博客(激动激动真激动!!!)A-B Problem

    #210. 差(A-B problem) 题目描述 楠楠在网上刷题,感觉第一题:求两数的和(A+B Problem)太无聊了,于是增加了一题:A-B Problem,难倒了一群小朋友,哈哈. 题目是这 ...