后缀数组模板

这篇博客详细介绍了后缀数组的概念,它是如何将字符串的所有后缀按字典序排序的。博主分享了使用二分+哈希的方法实现sort,并探讨了倍增和DC3两种构建后缀数组的复杂度。尽管DC3算法较难理解,但博主通过倍增算法给出了nlogn的时间复杂度实现。此外,还讲解了获取最长公共前后缀(LCP)的height数组。博客中提供了C++代码示例,并建议初学者观看视频辅助学习。
摘要由CSDN通过智能技术生成

第一次学后缀数组被难哭了 只能说大为震撼

后缀数组 可以将所有的后缀字典序进行排序
非常的巧妙 将后缀字典序排序
我们可以 使用 二分+哈希的方式 进行sort 重写 cmp
复杂度两个long*n
而后缀数组 有两种构建方法
倍增和DC3 DC3好难还没懂
写的是倍增
复杂度 n logn

里面数组非常晕 建议看视频学习 我太菜了
后面有最关键的一个证明

#include <bits/stdc++.h>
using namespace  std;
//#define  int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
#define fi first
#define se second
#define pb  push_back
#define inf 1<<62
#define endl "\n"
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define de_bug(x) cerr << #x << "=" << x << endl
#define all(a) a.begin(),a.end()
#define IOS   std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define  fer(i,a,b)  for(int i=a;i<=b;i++)
#define  der(i,a,b)  for(int i=a;i>=b;i--)
const int mod = 1e9 + 7;
const int N = 2e6 + 10;
int n, m , k;
int sa[N], x[N], h[N];
int c[N], y[N];
int rk[N];
string s;
// sa[i]排名第i位的是第几个后缀
// h[i]是sa[i]与sa[i - 1]的最长公共前缀的长度   lcp
// y是按第二关键字排的情况下每个后缀的排名
void get_sa() {
	for(int i = 1; i <= m; i++) c[i] = 0;
	//用桶排的方式去排序
	for(int i = 1; i <= n; i++) c[x[i] = s[i]]++;
	for(int i = 2; i <= m; i++) c[i] += c[i - 1];
	for(int i = n; i; i--) sa[c[x[i]]--] = i;

	//每一轮倍增进行后缀排序
	for(int k = 1; k <= n; k <<= 1) {
		int num = 0;
		for(int i = n - k + 1; i <= n; i++)y[ ++ num] = i;
		//长度不够k的 第二关键字 为空 所以优先排序
		//排名小于等于k的已经有序

		//第sa[i]-k个后缀的第二关键字为第sa[i]个后缀
		for(int i = 1; i <= n; i++)if(sa[i] > k)y[++num] = sa[i] - k;
		//已经按照第二关键字排序好了
		// y[i]表示以第二关键排序 排名为i的后缀的下标
		//x[y[i]]表示按 第一关键字的排名

		for(int i = 1; i <= m; i++)c[i] = 0;
		for(int i = 1; i <= n; i++)c[x[i]]++;
		for(int i = 1; i <= m; i++)c[i] += c[i - 1];
		for(int i = n; i ; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
		for (int i = 1; i <= n; i++) swap(x[i], y[i]);

		x[sa[1]] = 1, num = 1;
		//继续离散化  如果没有相同的话 即num==n
		for(int i = 2; i <= n; i++)
		    //如果第一关键字相同并且第二关键字也相同  那么我们就不能累计sun 
			x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
		if(num == n)break;
		m = num;
	}
}
// 有一个证明是   h[rk[i]]>=h[sa[rk[i]-1]]-1;
void get_height() {
	for(int i = 1; i <= n; i++)rk[sa[i]] = i;
	//rk[j]=i   第j个位置排名为i  sa[i]=j 排名为i的位置为j
	for(int i = 1, k = 0; i <= n; i++) {
		if(rk[i] == 1)continue;
		int j = sa[rk[i] - 1];
		if(k)k--;
		while(i + k <= n && j + k <= n && s[i + k] == s[j + k])k++;
		h[rk[i]] = k;
	}
}
void solve() {
	cin >> s;
	n = s.size();
	m = 'z';
	s = " " + s;
	get_sa();
	get_height();
	for(int i = 1; i <= n; i++)cout << sa[i] << " \n"[i == n];
	for(int i = 1; i <= n; i++)cout << h[i] << " \n"[i == n];
}
int main() {
	IOS;
	int _ ;
	cin >> _;
	while( _-- )
		solve();
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值