[达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树

来源:互联网 发布:监控软件流量 编辑:程序博客网 时间:2024/06/02 06:57

题意

给出一个长度为n的字符串,有m个询问a b c d表示求s[a..b]的子串和s[c..d]的最长公共前缀的最大值。
n,m<=100000

分析

先求一波后缀数组,然后考虑二分答案,将其转变成判定性问题。
设当前长度为len,那么我们可以在height数组内找到一个区间[L,R],使得该区间内的任何下标与s[c..d]的最长公共前缀不小于len。
然后我们可以对rank数组建一棵可持久化线段树,再查询rank[a,b-len+1]内有没有数在区间[L,R]内即可。
复杂度O(nlog2)

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int N=100005;int n,b[N],c[N],d[N],rank[N*2],sa[N],height[N],rmq[N][25],root[N],sz,m,lg[N];struct tree{int l,r,s;}t[N*20];char s[N];void get_sa(int n){    for (int i=1;i<=n;i++) b[s[i]]++;    for (int i=1;i<=n;i++) b[i]+=b[i-1];    for (int i=n;i>=1;i--) c[b[s[i]]--]=i;    int t=0;    for (int i=1;i<=n;i++)    {        if (s[c[i]]!=s[c[i-1]]) t++;        rank[c[i]]=t;    }    int j=1;    while (j<=n)    {        for (int i=1;i<=n;i++) b[i]=0;        for (int i=1;i<=n;i++) b[rank[i+j]]++;        for (int i=1;i<=n;i++) b[i]+=b[i-1];        for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;        for (int i=1;i<=n;i++) b[i]=0;        for (int i=1;i<=n;i++) b[rank[i]]++;        for (int i=1;i<=n;i++) b[i]+=b[i-1];        for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];        t=0;        for (int i=1;i<=n;i++)        {            if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++;            c[d[i]]=t;        }        for (int i=1;i<=n;i++) rank[i]=c[i];        if (t==n) break;        j*=2;    }    for (int i=1;i<=n;i++) sa[rank[i]]=i;}void get_height(int n){    int k=0;    for (int i=1;i<=n;i++)    {        if (k) k--;        int j=sa[rank[i]-1];        while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;        height[rank[i]]=k;    }}void get_rmq(){    int len=lg[n];    for (int i=1;i<=n;i++) rmq[i][0]=height[i];    for (int j=1;j<=len;j++)        for (int i=1;i<=n-(1<<j)+1;i++)            rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);}int get_min(int l,int r){    int len=r-l+1;    return min(rmq[l][lg[len]],rmq[r-(1<<lg[len])+1][lg[len]]);}void ins(int &d,int p,int l,int r,int x){    d=++sz;    t[d]=t[p];t[d].s++;    if (l==r) return;    int mid=(l+r)/2;    if (x<=mid) ins(t[d].l,t[p].l,l,mid,x);    else ins(t[d].r,t[p].r,mid+1,r,x);}int query(int d,int p,int l,int r,int x,int y){    if (x>y) return 0;    if (l==x&&r==y) return t[d].s-t[p].s;    int mid=(l+r)/2;    return query(t[d].l,t[p].l,l,mid,x,min(y,mid))+query(t[d].r,t[p].r,mid+1,r,max(mid+1,x),y);}bool check(int len,int a,int b,int c,int d){    int l=rank[c]+1,r=n;    while (l<=r)    {        int mid=(l+r)/2;        if (get_min(rank[c]+1,mid)>=len) l=mid+1;        else r=mid-1;    }    int R=l-1;    l=1,r=rank[c];    while (l<=r)    {        int mid=(l+r)/2;        if (get_min(mid+1,rank[c])>=len) r=mid-1;        else l=mid+1;    }    int L=r+1;    if (L>R) return 0;    if (query(root[b-len+1],root[a-1],1,n,L,R)) return 1;    else return 0;}int main(){    scanf("%d%d",&n,&m);    scanf("%s",s+1);    get_sa(n);    get_height(n);    for (int i=1;i<=n;i++) lg[i]=log(i)/log(2);    get_rmq();    for (int i=1;i<=n;i++) ins(root[i],root[i-1],1,n,rank[i]);    for (int i=1;i<=m;i++)    {        int a,b,c,d;        scanf("%d%d%d%d",&a,&b,&c,&d);        int l=1,r=min(b-a+1,d-c+1);        while (l<=r)        {            int mid=(l+r)/2;            if (check(mid,a,b,c,d)) l=mid+1;            else r=mid-1;        }        printf("%d\n",l-1);    }    return 0;}
0 0
原创粉丝点击