zoukankan      html  css  js  c++  java
  • BZOJ 4556(后缀数组+主席树求前驱后继+二分||后缀数组+二分+可持久化线段树)

    换markdown写了。。

    题意:

    给你一个1e5的字符串,1e5组询问,求([l_1,r_1])的所有子串与([l_2,r_2])的lcp

    思路:

    首先可以发现答案是具有单调性的,我们考虑二分答案,二分的范围显然为([0,min(r_2-l_2+1,r_1-l_1+1)])
    对于二分到的字符串长度mid,可以知道它的开头一定在([l_1,r_1-mid+1])中,这样满足了限定条件
    于是我们可以通过检查([l_1,r_1-mid+1])中是否有个值p,使得(lcp(rk[p],rk[l_2]) geq mid)即可判断答案

    法一:

    (lcp(i,j)=min(height[k]),i+1leq k leq j)(i,j为排名)可知
    为了使(lcp(rk[p],rk[l2]))尽量满足条件,rk[p]一定是目标区间内,(rk[l_2])的前驱或后继
    这样我们可以对(i,rk[i])建主席树,每次check查询([l_1,r_1-mid+1])(rk[l_2])的前驱后继
    但是问题来了,主席树查询前驱后继会退化。。(我乱讲的,可能是我不会写)
    虽然在bzoj上跑了16s过了,但是这显然不优秀

    update:退化的问题已经解决了:权值线段树剪枝的误解--以HDU6703为例

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<stack>
    #include<queue>
    #include<deque>
    #include<set>
    #include<vector>
    #include<map>
        
    #define fst first
    #define sc second
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson l,mid,root<<1
    #define rson mid+1,r,root<<1|1
    #define lc root<<1
    #define rc root<<1|1
    #define lowbit(x) ((x)&(-x)) 
    
    using namespace std;
    
    typedef double db;
    typedef long double ldb;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> PI;
    typedef pair<ll,ll> PLL;
    
    const db eps = 1e-6;
    const int mod = 1e9+7;
    const int maxn = 2e5+100;
    const int maxm = 2e6+100;
    const int inf = 0x3f3f3f3f;
    const db pi = acos(-1.0);
    
    
    int n,m;
    char s[maxn];
    int sa[maxn],rk[maxn],height[maxn];
    int y[maxn],x[maxn],c[maxn];
    void getSa(){
        for(int i=1;i<=n;i++)++c[x[i]=s[i]];
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1){
            int num = 0;
            for(int i=n-k+1;i<=n;i++)y[++num]=i;
            for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++)c[i]=0;
            for(int i=1;i<=n;i++)++c[x[i]];
            for(int i=2;i<=m;i++)c[i]+=c[i-1];
            for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;
            num=1;
            for(int i=2;i<=n;i++){
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
            }
            if(num==n)break;
            m=num;
        }
    }
    void getHeight(){
        int k=0;
        for(int i=1; i<=n; ++i)rk[sa[i]]=i;
        for(int i=1; i<=n; ++i){
            if(rk[i]==1) continue;
            if(k)--k;
            int j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])++k;
            height[rk[i]]=k;
        }
    }
    int d[maxn][26];
    void init(){
        for(int i = 1; i <= n; i++) d[i][0]=height[i];
        for(int j = 1; (1<<j) <= n; j++){
            for(int i = 1; i + (1<<j) - 1 <= n; i++){
                d[i][j] = min(d[i][j-1], d[i + (1<<(j-1))][j-1]);
            }
        }
    }
    int rmq(int l, int r){
        if(l>r)return -1;
        int k = 0;
        while((1<<(k+1)) <= r-l+1)k++;
        return min(d[l][k], d[r-(1<<k)+1][k]);
    }
    int q;
    
    int tot,root[maxn];
    int dat[maxn*40],ls[maxn*40],rs[maxn*40];
    int insert(int now, int l, int r, int x, int val){
        int p = ++tot;
        dat[p]=dat[now];ls[p]=ls[now];rs[p]=rs[now];
        if(l==r){dat[p]+=val;return p;}
        int mid = (l+r)>>1;
        if(x<=mid)ls[p]=insert(ls[now],l,mid,x,val);
        else rs[p]=insert(rs[now],mid+1,r,x,val);
        dat[p]=dat[ls[p]]+dat[rs[p]];
        return p;
    }
    int askmx(int x, int y, int l, int r, int k){//return rk|-1
        int sumr = dat[rs[y]]-dat[rs[x]];
        int mid = (l+r)>>1;
        if(dat[y]-dat[x]==0)return -1;
        if(l==r) return l;
        if(mid<k){
            int lRes = askmx(rs[x],rs[y],mid+1,r,k);
            if(lRes==-1)lRes=askmx(ls[x],ls[y],l,mid,k);
            return lRes;
        }
        else return askmx(ls[x],ls[y],l,mid,k);
        
    }
    int askmi(int x, int y, int l, int r, int k){
        int suml = dat[ls[y]]-dat[ls[x]];
        int mid = (l+r)>>1;
        if(dat[y]-dat[x]==0)return -1;
        if(l==r)return l;
        if(mid>k){
            int rRes=askmi(ls[x],ls[y],l,mid,k);
            if(rRes==-1)rRes=askmi(rs[x],rs[y],mid+1,r,k);
            return rRes;
        }
        else return askmi(rs[x],rs[y],mid+1,r,k);
    }
    bool ck(int x, int l1, int r1, int l2, int r2){
        int L = askmx(root[l1-1],root[r1-x+1],1,n,rk[l2]);
        int R = askmi(root[l1-1],root[r1-x+1],1,n,rk[l2]);
        if(L!=-1)L=rmq(L+1,rk[l2]);
        if(R!=-1)R=rmq(rk[l2]+1,R);
        return (L>=x||R>=x);
    }
    int main() {
        tot=0;
        scanf("%d %d", &n, &q);
        scanf("%s",s+1);
        m=122;n=strlen(s+1);
        getSa();
        getHeight();
        init();
        for(int i = 1; i <= n; i++){
            root[i]=insert(root[i-1],1,n,rk[i],1);
        }
        init();
        while(q--){
            int l1,r1,l2,r2;
            scanf("%d %d %d %d", &l1, &r1,&l2, &r2);
            int ans = 0;
            if(l2>=l1&&l2<=r1)ans=min(r2-l2+1,r1-l2+1);
            int l = 0, r = min(r1-l1+1,r2-l2+1);
            int res = 0;
            while(l<=r){
                int mid = (l+r)>>1;
                if(ck(mid,l1,r1,l2,r2)){
                    l=mid+1;
                    res=mid;
                }
                else r=mid-1;
            }
            ans=max(ans,res);
            printf("%d
    ",ans);
        }
        return 0;
    }
    /*
    8 1
    ththhtht
    1 6 7 8
    
    85 292 31 259
    79 299 5 232
     
    8 10
    aababbab
    1 1 1 1
    1 2 4 5
    1 5 4 5
    2 3 4 5
    1 5 2 5
    1 6 7 8
    3 6 1 7
    4 4 3 8
    1 5 1 8
    2 4 5 8
     */
    

    法二:

    既然(lcp(i,j))对于i向左以及j向右都是单调不增的,我们可以二分出rk的左右边界L以及R,使得(lcp(i,rk[l_2])geq mid)
    然后我们对于(i,sa[i])建可持久化线段树,查询区间((L+1,rk[l2]))中是否有坐标在([l_1,r_1-mid+1])
    这样复杂度是完美的(O(nlog^2n))

    代码:

    我好懒,没写代码

  • 相关阅读:
    ParamCount、ParamStr
    写一个可拖动的 TShape 回复 "韦韦" 的问题
    读十六进制文本到 Btye 数组的函数 回复 "峰哥!!!" 的问题
    网站速度优化
    [新功能]新增分类浏览页面
    [CN.Text开发笔记]嵌套Repeater的问题
    中秋祝福
    10 Golden rules for publishing your blog
    HttpCompressionModule 6的一个Bug及使用效果
    [CN.Text开发笔记]OnInit与运行期数据绑定
  • 原文地址:https://www.cnblogs.com/wrjlinkkkkkk/p/11506948.html
Copyright © 2011-2022 走看看