后缀数组解法:

先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树上查 \(sa[l..r]\) 是否有 \(a..b\) 中的任意一个数。时间复杂度 \(O(n\log^2 n)\)

\(Code\ Below:\)

#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+10;
int n,m,sa[maxn],tax[maxn],rnk[maxn],tp[maxn],h[maxn],f[maxn][18],g[maxn][18];
int T[maxn],L[maxn*20],R[maxn*20],sum[maxn*20],cnt;char s[maxn]; inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
} void SA(int m){
int i,j,k,p,d=0;
for(i=1;i<=n;i++) rnk[i]=s[i];
for(i=1;i<=n;i++) tax[rnk[i]]++;
for(i=1;i<=m;i++) tax[i]+=tax[i-1];
for(i=n;i>=1;i--) sa[tax[rnk[i]]--]=i;
for(k=1,p=0;p<n;m=p,k<<=1){
p=0;
for(i=n-k+1;i<=n;i++) tp[++p]=i;
for(i=1;i<=n;i++) if(sa[i]>k) tp[++p]=sa[i]-k;
for(i=1;i<=m;i++) tax[i]=0;
for(i=1;i<=n;i++) tax[rnk[i]]++;
for(i=1;i<=m;i++) tax[i]+=tax[i-1];
for(i=n;i>=1;i--) sa[tax[rnk[tp[i]]]--]=tp[i];
swap(rnk,tp);rnk[sa[1]]=p=1;
for(i=2;i<=n;i++) rnk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+k]==tp[sa[i]+k])?p:++p;
}
for(i=1;i<=n;i++) rnk[sa[i]]=i;
for(i=1;i<=n;i++){
p=sa[rnk[i]-1];if(d) d--;
while(s[i+d]==s[p+d]) d++;
h[rnk[i]]=d;
}
for(i=1;i<=n;i++) f[i][0]=h[i+1],g[i][0]=h[i];
for(j=1;j<=17;j++){
for(i=1;i+(1<<j)-1<=n;i++) f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(i=(1<<j);i<=n;i++) g[i][j]=min(g[i][j-1],g[i-(1<<(j-1))][j-1]);
}
} void update(int &now,int pre,int l,int r,int x){
now=++cnt;L[now]=L[pre];R[now]=R[pre];sum[now]=sum[pre]+1;
if(l == r) return ;
int mid=(l+r)>>1;
if(x <= mid) update(L[now],L[pre],l,mid,x);
else update(R[now],R[pre],mid+1,r,x);
} int query(int u,int v,int Le,int Ri,int l,int r){
if(Le <= l && r <= Ri) return sum[v]-sum[u];
int mid=(l+r)>>1,ans=0;
if(Le <= mid) ans+=query(L[u],L[v],Le,Ri,l,mid);
if(Ri > mid) ans+=query(R[u],R[v],Le,Ri,mid+1,r);
return ans;
} int check(int len,int x,int a,int b){
int l=x,r=x;
for(int i=17;i>=0;i--)
if(l-(1<<i)>=1&&g[l][i]>=len) l-=1<<i;
for(int i=17;i>=0;i--)
if(r+(1<<i)<=n&&f[r][i]>=len) r+=1<<i;
int ans=query(T[l-1],T[r],a,b-len+1,1,n);
return ans;
} int main()
{
n=read(),m=read();
scanf("%s",s+1);SA(128);
for(int i=1;i<=n;i++) update(T[i],T[i-1],1,n,sa[i]);
int a,b,c,d,l,r,mid,ans=0;
while(m--){
a=read(),b=read(),c=read(),d=read();
l=1;r=min(b-a+1,d-c+1);ans=0;
while(l<=r){
mid=(l+r)>>1;
if(check(mid,rnk[c],a,b)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}

后缀自动机解法:

因为后缀自动机只能处理后缀,所以我们将原串翻转一下,将最长前缀长度转化为最长后缀长度。然后我们对原串建一个后缀自动机,二分一下最长后缀长度 \(len\),从 \(pos[c]\) 出发倍增到 \(l[x]\geq len\) 的结点 \(x\),然后在 \(x\) 上查询 \(endpos\) 集合是否有 \(b+len-1..a\) 的任意一个数。维护 \(endpos\) 集合可以用线段树合并。

\(Code\ Below:\)

#include <bits/stdc++.h>
using namespace std;
const int maxn=500000+10;
int n,m,a[maxn],b[maxn],last,cnt,ch[maxn][26],fa[maxn],l[maxn];
int pos[maxn],f[maxn][21],T[maxn],L[maxn*60],R[maxn*60],sum[maxn*60],tot;
char s[maxn]; inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
} void insert(int c){
int p=last,q=++cnt;last=q;l[q]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=q;
if(!p) fa[q]=1;
else {
int r=ch[p][c];
if(l[p]+1==l[r]) fa[q]=r;
else {
int s=++cnt;l[s]=l[p]+1;
memcpy(ch[s],ch[r],sizeof(ch[r]));
fa[s]=fa[r];fa[r]=fa[q]=s;
for(;p&&ch[p][c]==r;p=fa[p]) ch[p][c]=s;
}
}
} void pushup(int now){
sum[now]=sum[L[now]]+sum[R[now]];
} void update(int &now,int l,int r,int x){
if(!now) now=++tot;
if(l == r){sum[now]++;return ;}
int mid=(l+r)>>1;
if(x <= mid) update(L[now],l,mid,x);
else update(R[now],mid+1,r,x);
pushup(now);
} int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
if(l == r){sum[x]+=sum[y];return x;}
int mid=(l+r)>>1,z=++tot;
L[z]=merge(L[x],L[y],l,mid);
R[z]=merge(R[x],R[y],mid+1,r);
pushup(z);
return z;
} int query(int now,int Le,int Ri,int l,int r){
if(!now) return 0;
if(Le <= l && r <= Ri) return sum[now];
int mid=(l+r)>>1,ans=0;
if(Le <= mid) ans+=query(L[now],Le,Ri,l,mid);
if(Ri > mid) ans+=query(R[now],Le,Ri,mid+1,r);
return ans;
} int check(int len,int x,int L,int R){
for(int i=20;i>=0;i--)
if(f[x][i]&&l[f[x][i]]>=len) x=f[x][i];
return query(T[x],L+len-1,R,1,n);
} int main()
{
n=read(),m=read();
scanf("%s",s+1);last=cnt=1;
reverse(s+1,s+n+1);
for(int i=1;i<=n;i++){
insert(s[i]-'a');pos[i]=last;
update(T[pos[i]],1,n,i);
}
for(int i=1;i<=cnt;i++) b[l[i]]++;
for(int i=1;i<=cnt;i++) b[i]+=b[i-1];
for(int i=1;i<=cnt;i++) a[b[l[i]]--]=i;
for(int i=cnt;i>=1;i--)
if(fa[a[i]]) T[fa[a[i]]]=merge(T[fa[a[i]]],T[a[i]],1,n);
for(int i=1;i<=cnt;i++) f[i][0]=fa[i];
for(int j=1;j<=20;j++)
for(int i=1;i<=cnt;i++) f[i][j]=f[f[i][j-1]][j-1];
int a,b,c,d,l,r,mid,ans;
while(m--){
a=n-read()+1,b=n-read()+1,c=n-read()+1,d=n-read()+1;
l=0,r=min(a-b+1,c-d+1),ans=0;
while(l<=r){
mid=(l+r)>>1;
if(check(mid,pos[c],b,a)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}

[HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)的更多相关文章

  1. 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 657  Solved: 274[Su ...

  2. 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ

    [BZOJ4556][Tjoi2016&Heoi2016]字符串 Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一 ...

  3. BZOJ4556 Tjoi2016&Heoi2016 字符串【后缀自动机+倍增+线段树合并】

    Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开 ...

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

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

  5. BZOJ 3083 树链剖分+倍增+线段树

    思路: 先随便选个点 链剖+线段树 1操作 就直接改root变量的值 2操作 线段树上改 3操作 分成三种情况 1.new root = xx 整个子树的min就是ans 2. lca(new roo ...

  6. HDU3710 Battle over Cities(最小生成树+树链剖分+倍增+线段树)

    Battle over Cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Othe ...

  7. BZOJ 4556 [HEOI2016/TJOI2016]字符串

    BZOJ 4556 [HEOI2016/TJOI2016]字符串 其实题解更多是用后缀数组+数据结构的做法,貌似也不好写. 反正才学了 sam 貌似比较简单的做法. 还是得先二分,然后倍增跳到 $ s ...

  8. 【bzoj4310】跳蚤 后缀数组+二分

    题目描述 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究. 首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个 ...

  9. 【BZOJ4556】字符串(后缀数组,主席树)

    [BZOJ4556]字符串(后缀数组,主席树) 题面 BZOJ 题解 注意看题: 要求的是\([a,b]\)的子串和[c,d]的\(lcp\)的最大值 先来一下暴力吧 求出\(SA\)之后 暴力枚举\ ...

随机推荐

  1. [html]Sublime Text添加插件

    今天想在Sublime Text(简称ST)内编写HTML后直接使用浏览器看效果,想添加View in Browser插件,然后遇到奇怪的问题添加插件直接报"找不到有用的插件" 一 ...

  2. 20155312 2016-2017-2 《Java程序设计》第七周学习总结

    20155312 2016-2017-2 <Java程序设计>第七周学习总结 课堂内容总结 read()每次读入一个字节. eg:short2个字节,2=0x0201,读入后要0x < ...

  3. HTML 内 meta标签

    <!-- 是否删除默认的苹果工具栏和菜单栏 --> <meta name="apple-mobile-web-app-capable" content=" ...

  4. 【转】Centos 7 修改主机名hostname

    在CentOS中,有三种定义的主机名:静态的(static),瞬态的(transient),和灵活的(pretty).“静态”主机名也称为内核主机名,是系统在启动时从/etc/hostname自动初始 ...

  5. 2019.01.17 bzoj1853: [Scoi2010]幸运数字(容斥+dfs)

    传送门 搜索菜题,然而第一次没有注意然后爆longlonglong longlonglong了. 题意:称所有数位由6,86,86,8组成的数为幸运数字,问一个一个区间[l,r][l,r][l,r]中 ...

  6. 2018.10.25 uoj#308. 【UNR #2】UOJ拯救计划(排列组合)

    传送门 有一个显然的式子:Ans=∑A(n,i)∗用i种颜色的方案数Ans=\sum A(n,i)*用i种颜色的方案数Ans=∑A(n,i)∗用i种颜色的方案数 这个东西貌似是个NPCNPCNPC. ...

  7. Centos7 yum install vim 出现“could not retrieve mirrorlist”

    ps:来源 https://www.cnblogs.com/justphp/p/5959655.html 办法一:改dns解析 vim /etc/resolv.conf 添加: nameserver ...

  8. 在平台中使用JNDI 数据源

    有些情况下为了系统的安全性考虑,可以将数据源配置成JNDI,在程序中只需要使用 容器的JNDI路径就可以了. 配置方法 1.在容器中配置数据源 <Context path="/&quo ...

  9. bzoj3262(cdq分治模板)

    裸的cdq,注意去重: #include<iostream> #include<cstdio> #include<cmath> #include<cstrin ...

  10. java基础-day9

    第09天 java集合 今日内容介绍 u 对象数组 u 集合类之ArrayList u 学生管理系统案例 第1章   对象数组 1.1      对象数组概述 A:基本类型的数组:存储的元素为基本类型 ...