试题 算法训练 后缀数组——最长重复子串

资源限制
时间限制: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名现在的排名。
    三者关系:
    试题 算法训练 后缀数组——最长重复子串_第1张图片

求后缀数组:

构造sa数组的方法一般有两种:

  • 倍增算法:O(nlogn)
  • DC3算法:O(n)

算法中会运用基数排序
倍增算法基数排序可自行去网上查阅了解。

LCP-最长公共前缀:

height[i]:表示suff[sa[i]]和suff[sa[i−1]]的最大公共前缀,也就是排名完后两个相邻的后缀的最长公共前缀。
图解:
试题 算法训练 后缀数组——最长重复子串_第2张图片

后缀数组的应用:

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

评测结果:

在这里插入图片描述

你可能感兴趣的:(蓝桥杯,c++)