题意
给一个字符串,求其中至少重复了 k 次的最长子串的长度。
思路
后缀数组的学习资料:
https://wenku.baidu.com/view/228caa45b307e87101f696a8.html
题目链接
http://poj.org/problem?id=3261
AC代码
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 20010;
int s[maxn];
int sa[maxn];
int t1[maxn], t2[maxn], c[maxn];
int rank[maxn], height[maxn];
void build_sa(int s[], int n, int m)
{
int *x = t1, *y = t2;
//第一次基数排序
for(int i= 0; i< m; i++) c[i] = 0;
for(int i= 0; i< n; i++) c[x[i] = s[i]] ++;
for(int i= 1; i< m; i++) c[i] += c[i - 1];
for(int i= n-1; i>= 0; i--) sa[--c[x[i]]] = i;
//以后的n次基数排序
for(int j= 1; j<= n; j<<= 1)
{
int p = 0;
for(int i= n-j; i< n; i++) y[p++] = i; //对第二关键字排序(利用前一次的sa)
for(int i= 0; i< n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
for(int i= 0; i< m; i++) c[i] = 0; //对第一关键字基数排序
for(int i= 0; i< n; i++) c[x[y[i]]] ++;
for(int i= 1; i< m; i++) c[i] += c[i-1];
for(int i= n-1; i>= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y); //本来保存在x中的上一次的rank转移到y中
p = 1, x[sa[0]] = 0; //求rank
for(int i= 1; i< n; i++)
x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+j] == y[sa[i]+j] ? p-1 : p++;
if(p >= n) break; //说明每一个后缀都有自己的rank,没有大小不分的了,排序结束
m = p; //更新最大值
}
}
void getHeight(int s[], int n) //求height(利用 height[i] >= height[i-1] - 1
{
int k = 0;
for(int i= 0; i<= n; i++) rank[sa[i]] = i;
for(int i= 0; i< n; i++)
{
if(k) k --;
int j = sa[rank[i] - 1];
while(s[i + k] == s[j + k]) k ++;
height[rank[i]] = k;
}
}
bool check(int n, int k, int t)
{
int num = 1;
for(int i= 2; i<= n; i++)
{
if(height[i] >= t)
{
num ++;
if(num >= k) return true;
}
else num = 1;
}
return false;
}
int main()
{
int n, k;
while(scanf("%d %d", &n, &k) == 2)
{
int Max = 0;
for(int i= 0; i< n; i++)
{
scanf("%d", &s[i]);
Max = max(Max, s[i]);
}
s[n] = 0; //注意
build_sa(s, n+1, Max+1);
getHeight(s, n);
int l = 0, r = n;
int ans = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(check(n, k, mid))
{
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
printf("%d\n", ans);
}
return 0;
}