Sort法 时间复杂度O(n*logn的平方)
板子:
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=500010;
int sa[maxn],rk[maxn],tmp[maxn];
string s;
int n,k;
bool cmp(int i,int j){
if(rk[i]!=rk[j]){
return rk[i]<rk[j];
}else{
int ri,rj;
ri=i+k<n?rk[i+k]:-1;
rj=j+k<n?rk[j+k]:-1;
return ri<rj;
}
}
void calc(){
for(int i=0;i<n;i++){
rk[i]=s[i];
sa[i]=i;
}
for(k=1;k<n;k*=2){
sort(sa,sa+n,cmp);
tmp[sa[0]]=0;
for(int i=0;i<n-1;i++){
tmp[sa[i+1]]=tmp[sa[i]]+cmp(sa[i],sa[i+1]);
}
for(int i=0;i<n;i++){
rk[i]=tmp[i];
}
}
}
int main() {
while(cin>>s){
n=s.size();
calc();
for(int i=0;i<n;i++){
cout<<sa[i]<<" ";
}
cout<<"\n";
}
}
基数排序(时间复杂度O(nlogn) 不会...)
后缀数组求高度数组
(这里的高度数组height[i]=lcp(S[i],S[rank[i]-1]))即sa数组中相邻的最长公共子串)
void Getheight() {
int j, k = 0;
for(int i = 1; i <= N; i++) {
if(k) k--;
if(rank[i]==0) continue;
int j = sa[rank[i] - 1];
while(s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
}
height数组性质:
性质1.height[ rank[i] ]≥height[ rk[i−1] ]−1
性质2.如果要求位置i的后缀的位置j的后缀的最长公共前缀 假设rank[i]<rank[j]那么
lcp(i,j) = min { height[x] | x ∈【 rank[i]+1, rank[j] 】}
height数组应用:
1.求任意两个后缀的最大公共前缀 :lcp(i,j) = min { height[x] | x ∈【 rank[i]+1, rank[j] 】}
2.求可重叠最长重复子串:height数组里的最大值
3.求本质不同子串个数: 枚举排序之后的字串,低于第i小的字串,它的左端点在sa[i]这个位置上。它们一它为端点一共可以组成n−sa[i]+1个串,但是如果答案一直累加上这个数的话是一定会有重复的,重复个数,一个串想要与当前串重复,一定是与当前串有公共字串。那么与sa[i]有公共字串的串的个数很显然是h[i] 所以答案是 n-sa[i]-h[i]+1 (i从1枚举到n)