Milk Patterns POJ - 3261 字尾陣列
阿新 • • 發佈:2018-11-19
題解
題目大意 給你一個長度為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;
}