Codeforces 666E Forensic Examination(广义后缀自动机+线段树合并)
将所有串(包括S)放一块建SAM。对于询问,倍增定位出该子串所在节点,然后要查询的就是该子串在区间内的哪个字符串出现最多。可以线段树合并求出该节点在每个字符串中的出现次数。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 1100010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
typedef pair<int,int> pii;
char s[N];
int n,m,Q,cnt=1,last,id[N],fail[N],len[N],root[N],f[N][22],q[N],tmp[N],tot=0;
map<int,int> son[N];
struct data{int l,r;pii t;
}tree[N*22];
pii operator +(const pii&a,const pii&b)
{
return make_pair(a.first+b.first,a.second);
}
pii Max(pii a,pii b){return a.first>b.first||a.first==b.first&&a.second<b.second?a:b;}
void ins(int &k,int l,int r,int x)
{
tree[++tot]=tree[k],k=tot;
if (l==r) {tree[k].t.first++;tree[k].t.second=x;return;}
int mid=l+r>>1;
if (x<=mid) ins(tree[k].l,l,mid,x);
else ins(tree[k].r,mid+1,r,x);
tree[k].t=Max(tree[tree[k].l].t,tree[tree[k].r].t);
}
int merge(int x,int y,int l,int r)
{
if (!x||!y) return x|y;
int k=++tot;
if (l==r) tree[k].t=tree[x].t+tree[y].t;
else
{
int mid=l+r>>1;
tree[k].l=merge(tree[x].l,tree[y].l,l,mid);
tree[k].r=merge(tree[x].r,tree[y].r,mid+1,r);
tree[k].t=Max(tree[tree[k].l].t,tree[tree[k].r].t);
}
return k;
}
pii query(int k,int l,int r,int x,int y)
{
if (!k||l==x&&r==y) return tree[k].t;
int mid=l+r>>1;
if (y<=mid) return query(tree[k].l,l,mid,x,y);
else if (x>mid) return query(tree[k].r,mid+1,r,x,y);
else return Max(query(tree[k].l,l,mid,x,mid),query(tree[k].r,mid+1,r,mid+1,y));
}
int extend(int p,int c,int i,int j)
{
int u;
if (son[p][c])
{
int q=son[p][c];
if (len[p]+1==len[q]) u=q;
else
{
int y=++cnt;
len[y]=len[p]+1;
son[y]=son[q];
fail[y]=fail[q],fail[q]=y;
while (son[p][c]==q) son[p][c]=y,p=fail[p];
u=y;
}
}
else
{
int x=++cnt;len[x]=len[p]+1;
while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
if (!p) fail[x]=1;
else
{
int q=son[p][c];
if (len[p]+1==len[q]) fail[x]=q;
else
{
int y=++cnt;
len[y]=len[p]+1;
son[y]=son[q];
fail[y]=fail[q],fail[q]=fail[x]=y;
while (son[p][c]==q) son[p][c]=y,p=fail[p];
}
}
u=x;
}
if (i==0) id[j]=u;
else ins(root[u],1,m,i);
return u;
}
int getpos(int k,int x)
{
for (int j=21;~j;j--) if (len[f[k][j]]>=x) k=f[k][j];
return k;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
scanf("%s",s+1);n=strlen(s+1);
last=1;for (int i=1;i<=n;i++) last=extend(last,s[i]-'a',0,i);
m=read();
for (int i=1;i<=m;i++)
{
scanf("%s",s+1);int _=strlen(s+1);
last=1;for (int j=1;j<=_;j++) last=extend(last,s[j]-'a',i,j);
}
for (int i=1;i<=cnt;i++) f[i][0]=fail[i];f[1][0]=1;
for (int j=1;j<=21;j++)
for (int i=1;i<=cnt;i++)
f[i][j]=f[f[i][j-1]][j-1];
for (int i=1;i<=cnt;i++) tmp[len[i]]++;
for (int i=1;i<=cnt;i++) tmp[i]+=tmp[i-1];
for (int i=1;i<=cnt;i++) q[tmp[len[i]]--]=i;
for (int i=cnt;i>=1;i--)
{
int x=q[i];
root[fail[x]]=merge(root[fail[x]],root[x],1,m);
}
Q=read();
while (Q--)
{
int l=read(),r=read(),pl=read(),pr=read();
int x=getpos(id[pr],pr-pl+1);
pii t=query(root[x],1,m,l,r);
printf("%d %d\n",max(l,t.second),t.first);
}
return 0;
}
Codeforces 666E Forensic Examination(广义后缀自动机+线段树合并)的更多相关文章
- Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)
题目链接 \(Description\) 给定串\(S\)和\(m\)个串\(T_i\).\(Q\)次询问,每次询问\(l,r,p_l,p_r\),求\(S[p_l\sim p_r]\)在\(T_l\ ...
- CF 666E Forensic Examination——广义后缀自动机+线段树合并
题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并
广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...
- Codeforces 666E Forensic Examination SAM or SA+线段树合并
E. Forensic Examination http://codeforces.com/problemset/problem/666/E 题目大意:给模式串S以及m个特殊串,q个询问,询问S的子串 ...
- [CF666E]Forensic Examination:后缀自动机+线段树合并
分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...
- CF666E Forensic Examination(后缀自动机+线段树合并)
给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串 ...
- Codeforces 666E Forensic Examination SAM+权值线段树
第一次做这种$SAM$带权值线段树合并的题 然而$zjq$神犇看完题一顿狂码就做出来了 $Orz$ 首先把所有串当成一个串建$SAM$ 我们对$SAM$上每个点 建一棵权值线段树 每个叶子节点表示一个 ...
- CF 666E Forensic Examination 【SAM 倍增 线段树合并】
CF 666E Forensic Examination 题意: 给出一个串\(s\)和\(n\)个串\(t_i\),\(q\)次询问,每次询问串\(s\)的子串\(s[p_l:p_r]\)在串\(t ...
随机推荐
- git一些常用配置
1. git config git config 分为全局和局部 全局:git config --glabol 局部:git config 最终会生成配置文件,一般在~/.gitconfig. 2. ...
- 2018-2019-2 《网络对抗技术》 Exp7 网络欺诈防范 20165222
1.实践目标 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法. 2.实践内容 (1)简单应用SET工具建立冒名网站 (1分) apachectl start开启服务 ser ...
- java udp通信
package net.kkxm.kms; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.n ...
- sklearn里计算roc_auc_score,报错ValueError: bad input shape
用sklearn的DecisionTreeClassifer训练模型,然后用roc_auc_score计算模型的auc.代码如下 clf = DecisionTreeClassifier(criter ...
- 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_01-用户认证需求分析
1.1 用户认证与授权 截至目前,项目已经完成了在线学习功能,用户通过在线学习页面点播视频进行学习.如何去记录学生的学习过程 呢?要想掌握学生的学习情况就需要知道用户的身份信息,记录哪个用户在什么时间 ...
- QML访问C++类内部
0.前提 C++类需要继承QObject,且有Q_OBJECT宏[所以QT自己的那么多类按道理上QML都是可以直接访问的,nb] 1.QML访问C++中的非private槽函数 可直接调用 信号也是 ...
- LeetCode_110. Balanced Binary Tree
110. Balanced Binary Tree Easy Given a binary tree, determine if it is height-balanced. For this pro ...
- 【微信小程序】wx.navigateBack() 携带参数返回
第一个页面: go_pick_time:function(e){ var that = this; var type = e.currentTarget.dataset.type; wx.naviga ...
- hadoop:/bin/bash: /bin/java: No such file or directory
Stack trace: ExitCodeException exitCode=127 In HADOOP_HOME/libexec/hadoop-config.sh look for the if ...
- PHP和MySQL.Web开发(原书第4版)学习盲点笔记
1.浏览器输出中文乱码解决: <?php header("Content-type: text/html; charset=utf-8"); ?> 相当于html中的: ...