后缀数组1

最长公共子串:
如果有兴趣就尝试一下吧~~
最长公共子串
这个题需要求后缀数组,简单说一下这个原理
找出所有以 i i i为起始,以 n − 1 n-1 n1为终止字符的后缀串,然后把他们用 s a sa sa r a n k rank rank和字典序同时控制排好,求最长公共子串的时候就是排名紧邻着的最长公共前缀,为啥?因为排名紧邻的一定是字典序最相近的,也最有可能产生最长公共子串,而排名这个过程,可以把它们变成 O ( n l g n ) O(nlgn) O(nlgn)的复杂度。之前暴力搜都是 O ( n 2 ) O(n^{2}) O(n2),最后求这个最长的前缀的时候,也可以把复杂度降成 O ( n ) O(n) O(n),其实最坏的话是 2 ∗ n 2*n 2n
再介绍两个定理:
s a [ i ] sa[i] sa[i]可以理解成老师要看排在第 i i i号位的学生是谁
r a n k [ i ] rank[i] rank[i]可以理解成第i号学生自己去询问成绩, r a n k rank rank这个数组相当于保护了学生的排名, s a sa sa就是连着的排名
s a sa sa r a n k rank rank是互逆的,比如
s a [ r a n k [ i ] ] = i sa[rank[i]]=i sa[rank[i]]=i,就是以一个学生从老师那问来了排名,再上教务去用自己的排名搜索自己的名字。哦不对,不要去教务处问可能问不到,还是去教务系统问吧(懂得都懂)
其实这个原型是求一个字符串的最长重复子串
还有一个题型是用哈希做的,是力扣的一道题:
不同的循环子字符串
如果求两个字符串的最长公共子串,就把他俩拼在一起呗
s a sa sa r a n k rank rank和计算 O ( n ) O(n) O(n)复杂度的函数都在下面代码里
(AC代码):

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
const int length = 1e5 + 5;
char p[length];
char s[length];
char dp[length << 2];
int sa[length << 2];
int height[length << 2];
int rank1[length << 2];
int tmp[length << 2];
int k;
int n;
void cal_sa();
void cal_height();
bool comp_sa(int a, int b)
{
	if (rank1[a] != rank1[b])
		return rank1[a] < rank1[b];
	else
	{
		int tmp1 = (a + k) < n ? rank1[a + k] : -1;
		int tmp2 = (b + k) < n? rank1[b + k] : -1;
		return tmp1 < tmp2;
	}
}
void cal_sa(int len)
{
	n = len;
	for (int i = 0; i < len; i++)
	{
		rank1[i] = (int)dp[i];
		sa[i] = i;
	}
	for (k = 1; k <= len; k = k * 2)
	{
		sort(sa, sa + len, comp_sa);
		tmp[sa[0]] = 0;
		for (int i = 1; i < len; i++)
		{
			tmp[sa[i]] = tmp[sa[i - 1]] + (comp_sa(sa[i - 1], sa[i]) ? 1 : 0);
		}
		for (int i = 0; i < len; i++)
		{
			rank1[i] = tmp[i];
		}
	}
}
void cal_height()
{
	int k = 0;
	for (int i = 0; i < n; i++)
	{
		rank1[sa[i]] = i;
	}
	for (int i = 0; i < n; i++)
	{
		if (rank1[i] == 0)continue;
		int j = sa[rank1[i] - 1];
		if (k != 0)
		{
			k--;
		}
		while (i + k < n&&j + k < n&&dp[i + k] == dp[j + k])k++;
		height[i] = k;
	}
}
int main(void)
{
	while (scanf("%s", p, sizeof(p)) != EOF)
	{
		scanf("%s", s, sizeof(s));
		int len1 = strlen(p);
		int len2 = strlen(s);
		memset(sa, 0, sizeof(sa));
		memset(height, 0, sizeof(height));
		memset(rank1, 0, sizeof(rank1));
		strcpy(dp, p);
		dp[len1] = '$';
		strcpy(&dp[len1 + 1], s);
		int len = len1 + len2 + 1;
		dp[len] = '\0';//把两个字符串拼接成一个字符串
		cal_sa(len);
		cal_height();
	//	int ans = *max_element(height, height + len);
		int max_len = -1;
		for (int i = 0; i < len; i++)
		{
			if (rank1[i] == 0)continue;
			int j = sa[rank1[i] - 1];
			if ((i<len1&&j>len1) || (i > len1&&j < len1))
				max_len = max(max_len, height[i]);
		}
		printf("%d\n", max_len);
	}
}

你可能感兴趣的:(算法题目,c++,字符串,后缀数组)