【CF666E】Forensic Examination(后缀自动机,线段树合并)
【CF666E】Forensic Examination(后缀自动机,线段树合并)
题面
翻译:
给定一个串\(S\)和若干个串\(T_i\)
每次询问\(S[pl..pr]\)在\(T_l..T_r\)中出现的最多次数,以及出现次数最多的那个串的编号。
题解
好题啊。
我们对于所有的\(T\)串构建出广义后缀自动机之后
把\(S\)丢到\(SAM\)上匹配,对于每组询问,
相当于回答\(S[pl..pr]\)所代表的节点的\(right\)集合所代表的串的众数是哪个串,以及这个众数出现的次数。
考虑如何维护\(right\)集合关于每个\(T\)的出现次数
我们对于每个节点开一棵线段树,线段树的值域是\(1..m\),用来维护每个\(T\)串的出现次数。
每个节点维护的值就是\(T\)串的最多出现次数。
这样就挺好做了,\(right\)集合只需要线段树合并就可以快速求得。
对于每个询问挂链,然后\(dfs\)一遍\(parent\)树的时候,顺便在线段树上查询一下区间最大就行了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1111111
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct SAM
{
struct Node
{
int son[26];
int ff,len;
}t[MAX];
int last,tot;
void init(){last=tot=1;}
void extend(int c)
{
int p=last,np=++tot;last=np;
t[np].len=t[p].len+1;
while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
if(!p)t[np].ff=1;
else
{
int q=t[p].son[c];
if(t[q].len==t[p].len+1)t[np].ff=q;
else
{
int nq=++tot;
t[nq]=t[q];
t[nq].len=t[p].len+1;
t[q].ff=t[np].ff=nq;
while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
}
}
}
}SAM;
int n,m,f[22][MAX];
struct Data{int v,p;}ans[MAX];
bool operator<(Data a,Data b){return (a.v<b.v)||(a.v==b.v&&a.p>b.p);}
struct SegmentTree{int ls,rs;Data v;}t[MAX<<4];
int Tot,rt[MAX];
struct query{int l,r,pl,pr;}q[MAX];
char S[MAX],T[MAX];
void Modify(int &x,int l,int r,int p)
{
if(!x)x=++Tot;
if(l==r){t[x].v.v++;t[x].v.p=p;return;}
int mid=(l+r)>>1;
if(p<=mid)Modify(t[x].ls,l,mid,p);
else Modify(t[x].rs,mid+1,r,p);
t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
}
void Merge(int &x,int y)
{
if(!x||!y){x|=y;return;}
if(!t[x].ls&&!t[x].rs){t[x].v.v+=t[y].v.v;return;}
Merge(t[x].ls,t[y].ls);Merge(t[x].rs,t[y].rs);
t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
}
Data Query(int x,int l,int r,int L,int R)
{
if(L==l&&r==R)return t[x].v;
int mid=(l+r)>>1;
if(R<=mid)return Query(t[x].ls,l,mid,L,R);
if(L>mid)return Query(t[x].rs,mid+1,r,L,R);
return max(Query(t[x].ls,l,mid,L,mid),Query(t[x].rs,mid+1,r,mid+1,R));
}
struct Link
{
struct Line{int v,next;}e[MAX];
int h[MAX],cnt;
void Add(int u,int v){e[++cnt]=(Line){v,h[u]};h[u]=cnt;}
}Par,Qy,Aw;
void dfs(int u)
{
for(int i=Par.h[u];i;i=Par.e[i].next)
dfs(Par.e[i].v),Merge(rt[u],rt[Par.e[i].v]);
for(int i=Aw.h[u];i;i=Aw.e[i].next)
ans[Aw.e[i].v]=Query(rt[u],1,m,q[Aw.e[i].v].l,q[Aw.e[i].v].r);
}
int main()
{
scanf("%s",S+1);n=strlen(S+1);
m=read();SAM.init();
for(int i=1;i<=m;++i)
{
SAM.last=1;scanf("%s",T+1);
for(int j=1,l=strlen(T+1);j<=l;++j)
SAM.extend(T[j]-97),Modify(rt[SAM.last],1,m,i);
}
int Q=read();
for(int i=1;i<=Q;++i)
{
q[i]=(query){read(),read(),read(),read()};
Qy.Add(q[i].pr,i);
}
for(int i=2;i<=SAM.tot;++i)Par.Add(f[0][i]=SAM.t[i].ff,i);
for(int i=1;i<22;++i)
for(int j=1;j<=SAM.tot;++j)f[i][j]=f[i-1][f[i-1][j]];
for(int i=1,nw=1,len=0;i<=n;++i)
{
int c=S[i]-97;
while(nw&&!SAM.t[nw].son[c])nw=SAM.t[nw].ff,len=SAM.t[nw].len;
if(!nw){nw=1,len=0;continue;}
nw=SAM.t[nw].son[c];len+=1;
for(int j=Qy.h[i];j;j=Qy.e[j].next)
{
int v=Qy.e[j].v,u=nw;
if(len<q[v].pr-q[v].pl+1)continue;
for(int k=21;~k;--k)
if(SAM.t[f[k][u]].len>=q[v].pr-q[v].pl+1)u=f[k][u];
Aw.Add(u,v);
}
}
dfs(1);
for(int i=1;i<=Q;++i)
{
if(!ans[i].v)ans[i].p=q[i].l;
printf("%d %d\n",ans[i].p,ans[i].v);
}
return 0;
}
【CF666E】Forensic Examination(后缀自动机,线段树合并)的更多相关文章
- 【Codeforces666E】Forensic Examination 后缀自动机 + 线段树合并
E. Forensic Examination time limit per test:6 seconds memory limit per test:768 megabytes input:stan ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- CF666E Forensic Examination(后缀自动机+线段树合并)
给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串 ...
- [CF666E]Forensic Examination:后缀自动机+线段树合并
分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...
- CF666E Forensic Examination [后缀自动机,线段树合并]
洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...
随机推荐
- Linux 安装FastDFS<单机版>(使用Mac远程访问)
阅读本文需要先阅读安装FastDFS<准备> 一 编译环境 yum install gcc-c++ yum -y install libevent yum install -y pcre ...
- Shiro 修改权限,刷新权限
shiro 访问鉴权:Realm AuthorizingRealm->doGetAuthorizationInfo doGetAuthorizationInfo protected abstra ...
- [redis] linux下安装篇(1)
一.redis是什么redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有 ...
- 6. 网络信息API
一.用数值表示socket地址&用名字表示socket地址(socket地址==>IP地址和端口号) 1. 用数值表示socket地址不便于记忆,也不便于扩展(比如从IPv4转移到IPv ...
- 3. IP地址转换函数
一.字符串表示的IP地址需要被转化为整数(二进制数)方能使用 IPv4地址:点分十进制字符串 IPv6地址:十六进制字符串 有时(如记录日志),我们则要把整数(二进制数)表示的IP地址转化为可读的字符 ...
- 20181016-4 Alpha阶段第1周/共2周 Scrum立会报告+燃尽图 04
此作业要求https://edu.cnblogs.com/campus/nenu/2018fall/homework/2248 Scrum master:徐常实 一.小组介绍 组长:王一可 组员:范靖 ...
- CS小分队第二阶段冲刺站立会议(5月28日)
昨日成果:昨天对我们的软件的主界面进行了思考,考虑到许多人建议我们团队添加可以自主增加软件快捷键的功能,我对这一想法的可行性和项目总体策划进行评估分析后,决定正式实施:已经完成从电脑上添加文件在我们的 ...
- 【分层最短路】Joyride
http://codeforces.com/gym/101873 C 多开一维状态记录时间,d[i][t] = 经过时间t走到节点i的最小花费 每一个状态分别向"原地等待"与&qu ...
- Java中按值传递与按引用传递的区别
值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响 ...
- 0527 SCRUM团队项目7.0
Sprint回顾 让我们一次比一次做得更好. 1.回顾组织 主题:“我们怎样才能在下个sprint中做的更好?” 时间:设定为1至2个小时. 参与者:整个团队. 场所:能够在不受干扰的情况下讨论. ...