CF666E Forensic Examination
思路
线段树合并+广义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的更多相关文章
- CF666E Forensic Examination SAM+倍增,线段树和并
题面: 给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数.如有多解输出最靠前的那一个. 分析: 第 ...
- CF666E Forensic Examination [后缀自动机,线段树合并]
洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线
传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...
- CF666E Forensic Examination(后缀自动机+动态线段树)
题意 给你一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l..p_r]$ 在 $T[l..r]$ 中的哪个串里的出现次数最多,并输出出现 ...
- CF666E Forensic Examination SAM+线段树合并+前缀树倍增
$ \color{#0066ff}{ 题目描述 }$ 给你一个串\(S\)以及一个字符串数组\(T[1..m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l..p_r]\)在\(T[l. ...
- CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增
题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ...
- CF666E Forensic Examination(后缀自动机+线段树合并)
给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串 ...
- CF666E Forensic Examination——SAM+线段树合并+倍增
RemoteJudge 题目大意 给你一个串\(S\)以及一个字符串数组\(T[1...m]\),\(q\)次询问,每次问\(S\)的子串\(S[p_l...p_r]\)在\(T[l...r]\)中的 ...
随机推荐
- XSS 与 CSRF 跨站攻击
先做个名词解释: XSS:跨站脚本(Cross-site scripting) CSRF:跨站请求伪造(Cross-site request forgery) 看了估计也不清楚什么意思吧? 那么,详细 ...
- 《图解HTTP》读书笔记(五:HTTP报文结构)
用于 HTTP 协议交互的信息被称为 HTTP 报文. 请求端(客户端) 的HTTP 报文叫做请求报文, 响应端(服务器端) 的叫做响应报文.HTTP 报文本身是由多行(用 CR+LF 作换行符) 数 ...
- 框架及其技术(Android)
Log框架: Logger: 简单,漂亮,强大的Android日志工具 Hugo: 在调试版本上注解的触发方法进行日志记录 Timber: 一个小的,可扩展的日志工具 响应式编程: RxJava ...
- mysql5安装
一.MYSQL的安装 1.打开下载的mysql安装文件mysql-5.0.27-win32.zip,双击解压缩,运行“setup.exe”. 2.选择安装类型,有“Typical(默认)”.“Comp ...
- pwn学习日记Day2 基础知识积累
知识杂项 shell-storm.org的shellcode数据库 使用pwntools库把shellcode作为输入传递给程序,尝试使用io.interactive()与程序进行交互,发现可以执行s ...
- deviceMotion.userAcceleration加速度方向
ios坐标系如图中所示,假设手机受到1个沿X正方向的力(假设该力产生了1m/s^2的加速度),那么deviceMotion.userAcceleration的值为多少呢? 答案是违反常识的:devic ...
- pandas处理时间序列(4): 移动窗口函数
六.移动窗口函数 移动窗口和指数加权函数类别如↓: rolling_mean 移动窗口的均值 pandas.rolling_mean(arg, window, min_periods=None, fr ...
- oo第二次博客总结
不知道怎么说好呢,自己对自己也很没有信心的,希望自己能做出些许改变
- 使用datagrid时json的格式
EasyUI的DataGrid要求返回的JSON数据集是这样的形式: {"total":总记录数量,"rows":[数据记录数组]}. 例如: {"t ...
- 判断是否引入jQuery,没有则引入
<script type="text/javascript"> window.jQuery || document .write("<script sr ...