蓝书(算法竞赛进阶指南)刷题记录——CH1402 后缀数组(二分+hash+快排)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/86417676

题目: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; 
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/86417676