Milk Patterns POJ - 3261 后缀数组

题解

题目大意 给你一个长度为n的序列 找出序列出现K次以上的最长后缀

使用后缀自动机将所有后缀排序 获得height数组 用set区间求最小值遍历一遍即可

AC代码

#include <stdio.h>
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 10;
int a[MAXN];
int n, r; //n字符串长度 r基数
int sa[MAXN]; //排名为i的后缀位置+1 i取值1~n
int cnt[MAXN]; //基数排序辅助数组
int rak[MAXN]; //第i个后缀的排名
int tmp[MAXN]; //rak的辅助数组
int heig[MAXN]; //后缀排序相邻LCP

void radix_sort(int *rk, int *tp)
{
	for (int i = 0; i <= r; i++) //清零
		cnt[i] = 0;
	for (int i = 1; i <= n; i++) //统计各桶要装入的数据个数
		cnt[rk[tp[i]]]++;
	for (int i = 1; i <= r; i++) //合并计算各个桶最后的数字下标索引
		cnt[i] += cnt[i - 1];
	for (int i = n; i >= 1; i--) //入桶 倒序保证稳定性
		sa[cnt[rk[tp[i]]]--] = tp[i]; //按照上次排序结果tmp保证稳定性的情况下再次根据rak排序
}
void suffix(int *a)
{
	int *rk = rak, *tp = tmp; //用指针指向两个数组以便后面交换数组
	for (int i = 1; i <= n; i++)
		rk[i] = a[i - 1], tp[i] = i; //初始化 rak、tmp为默认顺序
	r = 1e6 + 5; //char 0-127不算汉字
	radix_sort(rk, tp);
	for (int l = 1, p = 1, i; p < n; l <<= 1, r = p) //l + 1为当前子串长度 当p排序不同数量大于等于n时退出
	{
		//当前的tmp直接由上次的sa得到
		for (p = 0, i = n - l + 1; i <= n; i++) //长度越界后补0
			tp[++p] = i;
		for (i = 1; i <= n; i++)
			if (sa[i] > l)
				tp[++p] = sa[i] - l;
		radix_sort(rk, tp); //更新sa
		swap(rk, tp); //交换指针 用tmp存下上轮的rak
		rk[sa[1]] = p = 1; //用sa更新rak并离散 编号从1开始第一个定为1
		for (i = 2; i <= n; i++)
		{
			if (tp[sa[i]] != tp[sa[i - 1]] || tp[sa[i] + l] != tp[sa[i - 1] + l]) //压缩 相同子串记为同一编号
				p++;
			rk[sa[i]] = p; //rak与sa互逆
		}
	}
}
void get_height() //heig[i]为str[sa[i-1]]与str[sa[i]]的最长公共前缀
{
	for (int i = 1; i <= n; i++) //由于rak计算过程中是用指针 函数退出后不确定最后存在哪个数组重新计算
		rak[sa[i]] = i;
	int k = 0;
	for (int i = 1; i <= n; i++)
	{
		if (k) //heig[i] >= heig[i - 1] - 1
			k--;
		int j = sa[rak[i] - 1]; //以i-1为开头的串排序的前一位
		while (a[i - 1 + k] == a[j - 1 + k]) //字符串0~n-1
			k++;
		heig[rak[i]] = k; //存在i位置 有效值2~n
	}
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int K;
	cin >> n >> K;
	for (int i = 0; i < n; i++)
		scanf("%d", &a[i]);
	suffix(a);
	get_height();
	multiset<int> st;
	for (int i = 2; i < K; i++) //滑动区间最值
		st.insert(heig[i]);
	int ans = 0;
	for (int i = K; i <= n; i++)
	{
		if (i - K + 1 >= 2)
			st.erase(st.find(heig[i - K + 1]));
		st.insert(heig[i]);
		ans = max(ans, *st.begin());
	}
	cout << ans << endl;

	return 0;
}

猜你喜欢

转载自blog.csdn.net/CaprYang/article/details/83388979