Milk Patterns POJ - 3261 后缀数组

xiaoxiao2025-11-28  15

题解

题目大意 给你一个长度为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; }
转载请注明原文地址: https://www.6miu.com/read-5040149.html

最新回复(0)