CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增
题意:
给定一个串 $S$ 和若干个串 $T_{i}$
每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号.
数据范围: 需要离线
题解:
首先,很常规的对 $T_{1}$ 到 $T_{rmax}$ 的所有字符串构建一个广义后缀自动机.
来一遍线段树合并,合并的权值是每个 $T$ 串出现的次数.
合并完毕后,再广义后缀自动机上的每个点的线段树上都能查到有哪些串能覆盖当前串.
把询问按照右端点排序,将 $S$ 匹配到广义后缀自动机当中,并通过倍增来将每个询问串进行定位..
最后 DFS 一遍算出答案即可.
还是很优美 + 复杂的.
Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 2000000
#define N 30
#define sigma 26
#define inf 1000000000 using namespace std; vector <int> G[maxn];
char str[maxn],ss[maxn];
int str_len,m;
int ss_len[maxn];
int fa[maxn][27];
int ans1[maxn],ans2[maxn]; struct Query{ int l,r,ans,pl,pr,id; }arr[maxn];
int cmp(Query a,Query b) { return a.pr<b.pr; } namespace Seg{
struct Segment_Tree{ int l,r,maxv; }node[maxn<<2];
int root[maxn],nodes;
int newnode(){return ++nodes; }
void update(int p,int l,int r,int &o){
if(!o) o=newnode();
if(l==r) { node[o].maxv+=1; return ; }
int mid=(l+r)>>1;
if(p<=mid) update(p,l,mid,node[o].l);
else update(p,mid+1,r,node[o].r);
node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
int o=newnode(),mid=(l+r)>>1;
if(l==r) {
node[o].maxv=node[x].maxv+node[y].maxv;
return o;
}
node[o].l=merge(node[x].l,node[y].l,l,mid);
node[o].r=merge(node[x].r,node[y].r,mid+1,r);
node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv);
return o;
}
int query_max(int l,int r,int L,int R,int o) {
if(!o||l>r||r<L||l>R) return 0;
if(l>=L&&r<=R) return node[o].maxv;
int mid=(l+r)>>1,res=0;
res=max(res,query_max(l,mid,L,R,node[o].l));
res=max(res,query_max(mid+1,r,L,R,node[o].r));
return res;
}
int query_pos(int p,int l,int r,int L,int R,int o){
if(l>r||r<L||l>R||!o||node[o].maxv<p) return inf;
if(l==r) return l;
int mid=(l+r)>>1,a;
if((a=query_pos(p,l,mid,L,R,node[o].l))!=inf) return a;
else return query_pos(p,mid+1,r,L,R,node[o].r);
}
};
namespace SAM{
int f[maxn],dis[maxn],ch[maxn][N],tot,last;
int C[maxn],rk[maxn];
inline void init(){last=++tot; }
void ins(int c){
int p=last,np,nq;
if(ch[p][c]){
int q=ch[p][c];
if(dis[q]==dis[p]+1) last=q;
else
{
nq=++tot,last=nq;
f[nq]=f[q],f[q]=nq,dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q)ch[p][c]=nq,p=f[p];
}
}
else {
np=++tot,last=np,dis[np]=dis[p]+1;
while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
if(!p) f[np]=1;
else {
int q=ch[p][c];
if(dis[q]==dis[p]+1) f[np]=q;
else
{
nq=++tot;
f[nq]=f[q],f[q]=f[np]=nq,dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
}
}
}
}
}; int head[maxn],to[maxn],nex[maxn],edges;
void addedge(int u,int v) { nex[++edges]=head[u],head[u]=edges,to[edges]=v; }
void DFS(int u) {
for(int v=head[u];v;v=nex[v])
{
DFS(to[v]);
Seg::root[u]=Seg::merge(Seg::root[u],Seg::root[to[v]],1,m);
}
for(int i=0;i<G[u].size();++i) {
int cur=G[u][i];
int a=Seg::query_max(1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
ans1[arr[cur].id] = a;
if(!a) { ans2[arr[cur].id]=arr[cur].l; continue; }
int b=Seg::query_pos(a,1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
ans2[arr[cur].id] = (b==inf)?0:b;
}
} int main(){
//setIO("input");
//==============================================================================
scanf("%s",str+1),str_len = strlen(str + 1);
SAM::init();
for(int i=1;i<=str_len;++i) SAM::ins(str[i]-'a');
int i,j,queries;
for(SAM::init(),scanf("%d",&m),i=1;i<=m;++i)
{
scanf("%s",ss+1),ss_len[i] = strlen(ss+1);
for(SAM::last=1,j=1;j<=ss_len[i];++j)
{
SAM::ins(ss[j]-'a');
Seg::update(i,1,m,Seg::root[SAM::last]);
}
}
for(i=2;i<=SAM::tot;++i) addedge(SAM::f[i],i),fa[i][0]=SAM::f[i];
for(i=1;i<sigma;++i)
for(j=2;j<=SAM::tot;++j) fa[j][i]=fa[fa[j][i-1]][i-1];
for(scanf("%d",&queries),i=1;i<=queries;++i)
scanf("%d%d%d%d",&arr[i].l,&arr[i].r,&arr[i].pl,&arr[i].pr),arr[i].id=i;
sort(arr+1,arr+1+queries,cmp);
//=================================================================依次处理每个询问
int cur=1,p=1,q;
for(i=1;i<=queries;++i) {
for(;cur<=arr[i].pr;++cur) {
int c=str[cur]-'a';
while(p && !SAM::ch[p][c]) p=SAM::f[p];
if(!p) p=1;
else p=SAM::ch[p][c];
}
q=p;
if(SAM::dis[p]>=(arr[i].pr-arr[i].pl+1)) {
for(j=24;j>=0;--j) {
if(SAM::dis[fa[p][j]] >= (arr[i].pr-arr[i].pl+1)) p=fa[p][j];
}
G[p].push_back(i);
}
p=q;
}
DFS(1);
for(i=1;i<=queries;++i) printf("%d %d\n",ans2[i],ans1[i]);
return 0;
}
CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增的更多相关文章
- 【CF666E】Forensic Examination(后缀自动机,线段树合并)
[CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...
- CF666E Forensic Examination(后缀自动机+动态线段树)
题意 给你一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l..p_r]$ 在 $T[l..r]$ 中的哪个串里的出现次数最多,并输出出现 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- 【BZOJ3413】匹配(后缀自动机,线段树合并)
[BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...
- 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并
[CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...
- 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并
广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...
- CF 666E Forensic Examination——广义后缀自动机+线段树合并
题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- [CF666E]Forensic Examination:后缀自动机+线段树合并
分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...
随机推荐
- [ RESTful ] [ API ] 有用的資訊
1. 淺談 REST 軟體架構風格 (Part.I) - 從了解 REST 到設計 RESTful https://blog.toright.com/posts/725/representationa ...
- 更博不能忘——webpack学习笔记
webpack是前端构建系统就像grunt和gulp(之前有学习过grunt所以也就大概明白webpack是个什么东西),所以说前端技术大部分还真是相通的,现在觉得当时多看的东西并不白费,虽然长时间不 ...
- 【fiddler】 fiddler总是在菜单栏下面弹出提示“The system proxy was changed,click to reenable fiddler capture”--转
源地址:http://blog.csdn.net/htdeyanlei/article/details/52873060 标签: fiddler 2016-10-20 15:28 2747人阅读 评论 ...
- luoguP1002
p1002 题意: 从坐标A到坐标B的可能路线(有一些点不能走)情况,很明显可以看出用dp做 m[i][j]=m[i-1][j]+m[i][j-1](注意处理不能走的点) 自己在初始化时犯了错,第1行 ...
- input上传文件获取文件后缀名+select通过text选中option
1.input获取后缀名 var fileName = $("input[type='file']").val();//获取上传的文件(单个) var extName = file ...
- 百度图标echarts.js的使用
echarts官网:http://echarts.baidu.com/api.html#echarts echarts是百度公司开的一种开源制作图片工具,是一个专门制作图表的开源工具,功能非常强大,地 ...
- [Atcoder Code Festival 2017 Qual B Problem F]Largest Smallest Cyclic Shift
题目大意:给你\(A\)个a,\(B\)个b,\(C\)个c,要你构造一个字符串,使它的最小循环表示法最大.求这个表示法.解题思路:不知道怎么证,但把a.b.c当做单独的字符串扔进容器,每次把字典序最 ...
- 刷新页面vuex数据不消失和不跳转页面
先说点什么 vuex和路由拦截这一块捣鼓的有一段时间了,总算是爬出来了,特地来分享一下,首先声明没有什么基础介绍,用的是登录状态存储sessionStorage的方法!!! 进入正题 刷新 刷新相当与 ...
- 2015 Multi-University Training Contest 8 hdu 5384 Danganronpa
Danganronpa Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Tot ...
- 洛谷 P1490 买蛋糕
P1490 买蛋糕 题目描述 野猫过生日,大家当然会送礼物了(咳咳,没送礼物的同志注意了哈!!),由于不知道送什么好,又考虑到实用性等其他问题,大家决定合伙给野猫买一个生日蛋糕.大家不知道最后要买的蛋 ...