题意:
给定一个串 $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 广义后缀自动机_线段树合并_树上倍增的更多相关文章

  1. 【CF666E】Forensic Examination(后缀自动机,线段树合并)

    [CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...

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

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

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

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

  4. 【BZOJ3413】匹配(后缀自动机,线段树合并)

    [BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...

  5. 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并

    [CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...

  6. 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并

    广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...

  7. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

  8. 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

    题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...

  9. [CF666E]Forensic Examination:后缀自动机+线段树合并

    分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...

随机推荐

  1. 原生js简易日历效果实现

    这里我们将用原生js实现简易的日历,原理和之前的原生js选项卡差不多,不过也有些区别: 首先html代码: <div class="container"> <di ...

  2. Photoshop把图片调成固定的像素。

    1.用PhotoShop打开需要修改的图片. 2.点击“窗口”菜单的“图层”子菜单,打开图层控制面板(快捷键F7).3.用鼠标左键双击“图层”面板的“背景”图层.在弹出窗口中点击“确定”按钮,解锁背景 ...

  3. 决策树(Decision Trees)

    简介 决策树是一个预测模型,通过坐标数据进行多次分割,找出分界线,绘制决策树. 在机器学习中,决策树学习算法就是根据数据,使用计算机算法自动找出决策边界. 每一次分割代表一次决策,多次决策而形成决策树 ...

  4. Vue 做项目经验

    Vue 做项目经验 首先需要知道最基本的东西是: Vue 项目打包:npm run build Vue生成在网页上看的端口:npm run dev 修改端口号的地方在: config文件夹下index ...

  5. java内存管理之内存模型

    1,运行时数据区域 1. 程序计数器 (program counter register) 2. Java虚拟机栈 (jvm stack) 3. 本地方法栈 (native method stack) ...

  6. ZJU 2671 Cryptography

    Cryptography Time Limit: 5000ms Memory Limit: 32768KB This problem will be judged on ZJU. Original I ...

  7. 【转】 C# ListView实例:文件图标显示

    [转] C# ListView实例:文件图标显示 说明:本例将目录中的文件显示在窗体的ListView控件中,并定义了多种视图浏览.通过调用Win32库函数实现图标数据的提取. 主程序: 大图标: 列 ...

  8. 兴趣爱好-QQ的本地共享

    QQ这个本地共享简直了,不就实现了公网FTP的功能么?好方便啊,有啥文件共享给好友就直接放在本地就可以了,真好用

  9. spring mvc过滤器filter

    SpringMVC 过滤器Filter使用解析 1.如上所示的spring-web.jar包结构所示, Spring的web包中中提供有很多过滤器,这些过滤器位于org.springframework ...

  10. [环境搭建] VS-Visual Studio-IIS Express 支持局域网訪问

    原创作品,转载请注明出处:http://blog.csdn.net/qiujuer/article/details/40350385 使用Visual Studio开发Web网页的时候有这种情况:想要 ...