1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——CH1402 字尾陣列(二分+hash+快排)

藍書(演算法競賽進階指南)刷題記錄——CH1402 字尾陣列(二分+hash+快排)

題目:CH1401.
題目大意:求出一個字串的所有後綴的排名,以及排名為i與i-1的字尾的最長公共字首LCP.

這道題一看是字尾陣列裸題,但是我不會 為了學習hash,我們考慮hash的寫法.

考慮SA的求法,顯然直接排序的串長是 O ( n 2 )

O(n^2) 的,所以總複雜度會達到 O ( n 2 l o g
n ) O(n^2logn)
直接TLE.那麼考慮用Hash優化串的比較過程,想到兩個串的大小比較由第一個不相等的字元決定,也就是由兩個串最長公共字首LCP的後一個字元決定,所以二分LCP長度即可.時間複雜度 O ( n
l o g 2 n ) O(nlog^2n)
.

再來考慮Height陣列的求法,發現Height陣列的本質就是求n-1次LCP,所以我們也可以使用二分求LCP做到 O ( n l o g n ) O(nlogn) 解決這個問題.

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;
typedef unsigned long long ULL;

const int N=300000;
const ULL P=131;

char c[N+9];
int n,sa[N+9],hei[N+9];
ULL s[N+9],Pow[N+9];

ULL Hash(ULL *s,int l,int r){return s[r]-s[l-1]*Pow[r-l+1];}

int LCP(int a,int b){
  int l=0,r=min(n-a+1,n-b+1);
  for (int mid=l+r>>1;l+1<r;mid=l+r>>1)
    Hash(s,a,a+mid-1)==Hash(s,b,b+mid-1)?l=mid:r=mid;
  return Hash(s,a,a+r-1)==Hash(s,b,b+r-1)?r:l;
}

bool cmp(const int &a,const int &b){
  int lcp=LCP(a,b); 
  return c[a+lcp]<c[b+lcp];
}

Abigail into(){
  scanf("%s",c+1);
  n=strlen(c+1);
}

Abigail work(){
  Pow[0]=1;
  for (int i=1;i<=n;++i){
    sa[i]=i;
    s[i]=s[i-1]*P+c[i];
    Pow[i]=Pow[i-1]*P;
  }
  sort(sa+1,sa+1+n,cmp);
  for (int i=2;i<=n;++i)
    hei[i]=LCP(sa[i],sa[i-1]);
}

Abigail outo(){
  for (int i=1;i<n;++i)
    printf("%d ",sa[i]-1);
  printf("%d\n",sa[n]-1);
  for (int i=1;i<n;++i)
    printf("%d ",hei[i]);
  printf("%d\n",hei[n]);
}

int main(){
  into();
  work();
  outo();
  return 0; 
}