题意:
给定一个串 $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. 微信小程序遇坑笔记

    最近做了一个简单的微信小程序,遇到了一些坑: 1.appid固定,但是appsecret是可以变的,而且没有地方查看,后台以查看就是变更了,所以这个地方在开发的时候需要保存好: 2.打开网页,这个网页 ...

  2. table标签 在谷歌和ie浏览器下不同的表现效果

    在项目中有了一个这样的需求: 我需要利用vue的模板语法v-for循环生成tr,这个tr是需要双重循环来确定其个数的, 我的实现: 我在tr外面包了一个template标签, 效果: 谷歌浏览器下实现 ...

  3. day15 函数对象以及作用域

    目录 函数对象 函数对象的四大功能 函数的嵌套 名称空间和作用域 名称空间 考试必考 作用域 global nonlacal LEGB原则 函数对象 ==Python中一切皆对象== 函数名也可以看成 ...

  4. ntp.log日志梳理

    [日志]offset 正负 机器A上执行: remote refid st t when poll reach delay offset jitter ======================== ...

  5. qt 透明化方法汇总

    一. QT 透明设置 背景,标题栏透明,下级Widget,painter绘出来的(比如,drawtext,drawline)不透明 QWidget window; window.setWindowFl ...

  6. 2019-03-18 OpenCV Tesseract-OCR 下载 安装 配置(cv2 报错)

    OpenCV 下载 安装 配置 1.下载和Python版本对应的版本,此为下载地址 2.安装(在powershell管理员模式下安装) pip3 install .\opencv_python-3.4 ...

  7. ArcGIS 安装

    百度网盘下载链接 密码:tvm6 打开解压的文件后,第一步为安装licence manager(安装监听) 打开\ArcGIS10.4\LicenseManager中的Setup.exe 傻瓜式安装 ...

  8. shell 测试命令

    一.使用 test 命令可以对文件.字符串等进行测试,一般配合控制语句使用. 1.字符串测试 test str1 = str2 //测试字符串是否相等 test str1 != str2 //测试字符 ...

  9. Mybatis动态代理实现函数调用

    如果我们要使用MyBatis进行数据库操作的话,大致要做两件事情: 1. 定义DAO接口 在DAO接口中定义需要进行的数据库操作. 2. 创建映射文件 当有了DAO接口后,还需要为该接口创建映射文件. ...

  10. HDU 4253 Two Famous Companies

    Two Famous Companies Time Limit: 15000ms Memory Limit: 32768KB This problem will be judged on HDU. O ...