资源限制
时间限制:100ms 内存限制:256.0MB
问题描述
给定一个长度为n的数串,求至少出现k 次的最长重复子串的长度,这k 个子串可以重叠。保证有子串出现至少k次。
输入格式
第一行:两个整数n, k;
第二行:2到n + 1行:n个整数,这n个整数组成了一个数串。
输出格式
一个整数,表示最长重复子串的长度。
样例输入
8 2
1 2 3 2 3 2 3 1
样例输出
4
数据规模和约定
0 ≤ n ≤ 20000, 2 ≤ k ≤ n,0≤ 数串中的整数≤ 1000000
后缀数组:
- 用suff[i]:表示以第i位为开头的后缀。
- 后缀数组sa[i]:表示所有后缀在排完序后,排名为i的后缀在原串中的位置。
- 名次数组rank[i]:表示所有后缀在排序完后,原字符串中第i名现在的排名。
三者关系:
求后缀数组:
构造sa数组的方法一般有两种:
- 倍增算法:O(nlogn)
- DC3算法:O(n)
算法中会运用基数排序
倍增算法和基数排序可自行去网上查阅了解。
LCP-最长公共前缀:
height[i]:表示suff[sa[i]]和suff[sa[i−1]]的最大公共前缀,也就是排名完后两个相邻的后缀的最长公共前缀。
图解:
后缀数组的应用:
1、可重叠最长重复子串
给定一个字符串,求最长重复子串,这两个子串可以重叠
2、不可重叠最长重复子串
给定一个字符串,求最长重复子串,这两个子串不可以重叠
3、可重叠的K次最长重复子串
给定一个字符串,求至少出现K次的最长重复子串,这K个子串可以重叠
能力有限,可自行了解:后缀数组——处理字符串的有力工具
本题源程序:
#include<iostream>
using namespace std;
#define maxsize 100000
int Rank[maxsize], sa[maxsize];
int height[maxsize];
int sec[maxsize], t[maxsize];
int s[maxsize];//接收用户输入的数组
int num = maxsize;
int len, number;//长度和次数
inline void SA() //获得sa[]数组
{
for (int i = 1; i <= num; i++) t[i] = 0;
for (int i = 1; i <= len; i++) ++t[Rank[i] = s[i]];
for (int i = 1; i <= num; i++) t[i] += t[i - 1];
for (int i = len; i >= 1; i--) sa[t[Rank[i]]--] = i;
for (int k = 1; k <= len; k++)
{
int cnt = 0;
for (int i = len - k + 1; i <= len; i++) sec[++cnt] = i;
for (int i = 1; i <= len; i++) if (sa[i] > k) sec[++cnt] = sa[i] - k;
for (int i = 1; i <= num; i++) t[i] = 0;
for (int i = 1; i <= len; i++) ++t[Rank[i]];
for (int i = 1; i <= num; i++) t[i] += t[i - 1];
for (int i = len; i >= 1; i--) sa[t[Rank[sec[i]]]--] = sec[i], sec[i] = 0;
swap(Rank, sec);
Rank[sa[1]] = 1, cnt = 1;
for (int i = 2; i <= len; i++)
Rank[sa[i]] = (sec[sa[i]] == sec[sa[i - 1]] && sec[sa[i] + k] == sec[sa[i - 1] + k]) ? cnt : ++cnt;
if (cnt == len) break;
num = cnt;
}
}
void Getheight() //获得height[]数组
{
int j, k = 0;
for (int i = 1; i <= len; 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 mid)//利用height[]数组进行分类,然后统计组内是否满足重复的次数。
{
int group = 0;
for (int i = 1; i <= len; i++)
{
if (height[i] >= mid)
group++;
if (group >= number)
return true;
if (height[i] < mid)
group = 1;
}
return false;
}
int main()
{
cin >> len >> number;
for (int i = 1; i <= len; i++)
cin >> s[i];
SA();
Getheight();
int front=1, rear=len;
int maxlen = 0;
while (front <= rear)//二分法!
{
int mid = (front + rear) / 2;
if (check(mid))
{
front = mid + 1;
maxlen = mid;
}
else
rear = mid - 1;
}
cout << maxlen << endl;
}