1. 程式人生 > >Milk Patterns POJ - 3261 字尾陣列

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; }