• hdu 6704 后缀数组height数组上建立ST表二分求sa数组上区间第K大


    给一个长为n的字符串,m次询问,每次求子串[l,r]第k次出现的起点位置

    做法:

    数据量很大,输入的字符串/询问总量可以达到1e5*5,必须尽量实现单次$O(logn)$的查询和至多$O(nlogn)$的预处理

    1.子串[l,r]一定是某个后缀的前缀,而"后缀的前缀的重复出现"这个问题可以很容易想到后缀数组的height

    2.考虑重复出现,显然一个后缀的长为r-l+1的前缀的出现位置即为height数组上一段连续的大于等于r-l+1的区间

    3.得到这个区间后,就变成了一个在sa数组上求区间第k小的问题了

    那么做法就是:

    预处理:

    1.预处理height数组

    2.预处理height数组区间min的st表

    3.预处理支持查询sa数组上的区间kth的一棵可持久化线段树

    处理询问:

    1.我们从后缀数组的rk[l],直接锁定一个合法的起始位置,

    2.然后利用st表快速二分出一个合法的连续区间,满足min>=r-l+1,

    3.在可持久化线段树上查询这个区间的kth小

     总复杂度:$O((m+n)logn)$

    (说起来挺复杂,但是也就130行)

    #include<bits/stdc++.h>
    #define ll long long
    #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
    #define per(ii,a,b) for(int ii=b;ii>=a;--ii)
    using namespace std;//head
    const int maxn=1e6+10,maxm=3e5+10;
    const ll INF=0x3f3f3f3f,mod=1e9+7;
    int casn,n,m,k;
    const int csize=128;
    char s[maxn];
    namespace suffix{
      int sa[maxn],h[maxn],rank[maxn];
      int x[maxn],y[maxn],c[maxn];
       void geth(int n){
        int j,k=0;
        rep(i,1,n)rank[sa[i]]=i;
        rep(i,1,n){
          if(k)k--;
          j=sa[rank[i]-1];
          while(s[i+k]==s[j+k])k++;
          h[rank[i]]=k;
        }
      }
      void getsa(int n,int m){
        rep(i,1,m)c[i]=0;
        rep(i,1,n)c[x[i]=s[i]]++;
        rep(i,1,m)c[i]+=c[i-1];
        per(i,1,n)sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1){
          int p=1;
          rep(i,n-k+1,n)y[p++]=i;
          rep(i,1,n) if(sa[i]>=k+1)y[p++]=sa[i]-k;
          rep(i,1,m)c[i]=0;
          rep(i,1,n)c[x[y[i]]]++;
          rep(i,1,m)c[i]+=c[i-1];
          per(i,1,n)sa[c[x[y[i]]]--]=y[i];
          swap(x,y);
          p=1;x[sa[1]]=1;
          rep(i,2,n)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p:++p;
          if(p>=n)break;
          m=p;
        }
        geth(n);
      }
    }
    const int maxp=20;
    class stable{public:
      int logn[maxn],dp[maxp][maxn];
      void init(int n=maxn-1){
        logn[2]=1;
        rep(i,3,n) logn[i]=logn[i>>1]+1;
      }
      void cal(int *a,int n){//init(n)
        rep(i,1,n) dp[0][i]=a[i];
        rep(j,1,maxp) for(int i=1;i+(1<<j)-1<=n;++i)
          dp[j][i]=min(dp[j-1][i],dp[j-1][i+(1<<(j-1))]);
      }
      inline int query(int l,int r){
        int lg=logn[r-l+1];
        return min(dp[lg][l],dp[lg][r-(1<<lg)+1]);
      }
    }st;
    namespace tree{
    #define nd seg[now]
    #define ndp seg[pre]
    #define mid ((s+t)>>1)
      int rt[maxn],size;
      struct node{int l,r,sum;}seg[maxn*30];
      void init(){
        size=0;fill_n(rt,n+1,0);
      }
      void maketree(int s=1,int t=n,int &now=rt[0]){
          now=++size;nd={s,t,0};
          if(s==t) return ;
          maketree(s,mid,nd.l);maketree(mid+1,t,nd.r);
      }
      void update(int &now,int pre,int k,int s=1,int t=n){
          now=++size;nd=ndp,nd.sum++;
          if(s==t) return ;
          if(k<=mid)update(nd.l,ndp.l,k,s,mid);
          else update(nd.r,ndp.r,k,mid+1,t);
      }
      int query(int now,int pre,int k,int s=1,int t=n){
          if(s==t) return s;
          int sum=seg[ndp.l].sum-seg[nd.l].sum;
          if(k<=sum) return query(nd.l,ndp.l,k,s,mid);
          else return query(nd.r,ndp.r,k-sum,mid+1,t);
      }
      inline int kth(int l,int r,int k){
        return query(rt[l-1],rt[r],k);
      }
      inline void up(int a,int x){
        update(rt[a],rt[a-1],x);
      }
    #undef mid
    };
    int query(int s,int t,int k){
      int pos=suffix::rank[s];
      int l=1,r=pos-1,len=t-s+1;
      int s0=pos,t0=pos;
      while(l<=r){
        int mid=(l+r)>>1;
        if(st.query(mid+1,pos)>=len) s0=mid,r=mid-1;
        else l=mid+1;
      }
      l=pos+1,r=n;
      while(l<=r){
        int mid=(l+r)>>1;
        if(st.query(pos+1,mid)>=len) t0=mid,l=mid+1;
        else r=mid-1;
      }
      if(t0-s0+1<k) return -1;
      else return tree::kth(s0,t0,k);
    }
    int main() {IO;
      cin>>casn;
      st.init();
      while(casn--){
        cin>>n>>m>>(s+1);
        suffix::getsa(n,128);
        st.cal(suffix::h,n);
        tree::init();
        tree::maketree(1,n);
        rep(i,1,n) tree::up(i,suffix::sa[i]);
        while(m--) {
          int l,r,k;cin>>l>>r>>k;
          cout<<query(l,r,k)<<endl;
        }
      }
    }
    
  • 相关阅读:
    利用高斯金字塔制作图像显著图
    python实现图像高斯金字塔
    使用差分金字塔提取图像边缘 python实现
    使用色彩追踪和形态学运算得到图像中感兴趣区域
    图像掩膜讲解
    图像色彩追踪
    手动绘制方向梯度直方图(HOG)
    Zhang-Suen 图像细化算法python实现
    hilditch图像细化算法python实现
    P2256 一中校运会之百米跑
  • 原文地址:https://www.cnblogs.com/nervendnig/p/11437755.html
Copyright © 2020-2023  润新知