Skip to content

后缀数组 ​

cpp
struct SuffixArray{
	int n;
	vector<int> sa,rk,lc;
	SuffixArray(const string &s){
		n=s.length();
		sa.resize(n);//sa[i]为排名为第i位的后缀
		rk.resize(n);//rk[i]为第i位后缀的排名
		lc.resize(n);//lc为排名第i位与i-1位的最长公共前缀
		//lcp(i,i)=len(i),lcp(i,j)=min(lcp(i,k),lcp(k,j))
		iota(sa.begin(),sa.end(),0);
		sort(sa.begin(),sa.end(),[&](int a,int b){return s[a]<s[b];});
		//先按照每一位排
		rk[sa[0]]=0;
		for(int i=1;i<n;i++){
			rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
		}
		int k=1;
		vector<int> tmp,cnt(n);//tmp用来存取第二关键字排序,cnt用于第一关键字的基数排序
		while(rk[sa[n-1]]<n-1){
			tmp.clear();
			//后面k个数,第二关键字必定为空,排前面
			for(int i=0;i<k;i++){
				tmp.push_back(n-k+i);
			}
			//剩下n-k个数,第二关键字必定为之前排序的第一关键字的顺序
			for(auto i:sa){
				if(i>=k)tmp.push_back(i-k);
			}
			fill(cnt.begin(),cnt.end(),0);
			for(int i=0;i<n;i++)cnt[rk[i]]++;
			for(int i=1;i<n;i++)cnt[i]+=cnt[i-1];
			for(int i=n-1;i>=0;i--)sa[--cnt[rk[tmp[i]]]]=tmp[i];
			swap(rk,tmp);
			rk[sa[0]]=0;
			for(int i=1;i<n;i++){
				rk[sa[i]]=rk[sa[i-1]]+(tmp[sa[i-1]]<tmp[sa[i]]||sa[i-1]+k==n||tmp[sa[i-1]+k]<tmp[sa[i]+k]);
				/*
				1. 都有第二关键字,如果第一关键字相等,会在第三次判断,否则在第一次判断
				2. 都没有第二关键字,在第一次判断
				3. 前面没有,后面有,第二次判断
				*/
			}
			k*=2;
		}
		//对于第i个后缀和第i-1个后缀,有lc[rk[i]]>=lc[rk[i-1]]-1
		//k最多加2*n次,得到线性做法
		for(int i=0,k=0;i<n;i++){
			if(rk[i]==0)continue;
			if(k)k--;
			int j=sa[rk[i]-1];
			while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
			lc[rk[i]]=k;
		}
	}
};