题目大概意思就是 给两个字符串,求最长公共字符串子串长度
我们可以考虑用后缀数组和高度数组
一个字符串 中 最长的两个相同字符串长度, 不就是 后缀数组中相邻两个后缀的最长公共前缀, 不就是 高度数组的最大值
这不就与题意很相似了,我们只需要把两个字符串连接在一起就可以了
然后我们比较高度数组的大小即可
需要注意:
- 因为比较高度数组时,可能两个相邻的后缀是在同一个字符串中,所以我们需要先判断后缀是从哪开始的
- 这时候又会出现另一种情况,可能有个后缀经过了两个字符串,这种情况判断,两个后缀是从不同字符串开始的,无法判断出来,所以我们要在连接两个字符串时,在中间加上一个在字符串中不会出现的字符 ( 例:S = a + ‘\0’ + b )
#include <iostream>
#include <stdio.h>
#include <string>
#include <algorithm>
using namespace std;
string S;
int sa[20005], rk[20005], tmp[20005], lcp[20005];
int k;
bool cmp(int a, int b)
{
if(rk[a] == rk[b])
{
int i = a + k <= S.length() ? rk[a + k] : -1;
int j = b + k <= S.length() ? rk[b + k] : -1;
return i < j;
}
return rk[a] < rk[b];
}
void construct_sa()
{
for(int i = 0; i <= S.length(); i++)
{
sa[i] = i;
rk[i] = i < S.length() ? S[i]: -1;
}
for(int i = 1; i <= S.length(); i = i * 2)
{
k = i;
sort(sa, sa + S.length() + 1, cmp);
tmp[sa[0]] = 0;
for(int i = 1; i <= S.length(); i++)
{
tmp[sa[i]] = tmp[sa[i-1]] + (cmp(sa[i-1], sa[i]) ? 1 : 0);
}
for(int i = 0; i <= S.length(); i++)
{
rk[i] = tmp[i];
}
}
}
void construct_lcp()
{
int h = 0;
for(int i = 0; i < S.length(); i++)
{
int j = rk[i] - 1;
if(h != 0) h--;
while(i + h < S.length() && sa[j] + h < S.length() && S[sa[j]+h] == S[i+h]) h++;
lcp[j] = h;
}
}
int main()
{
int T;
scanf("%d", &T);
getchar();
while(T--)
{
string a, b;
getline(cin, a);
getline(cin, b);
int n = a.length();
S = a + '\0' + b;
construct_sa();
construct_lcp();
int res = 0;
for(int i = 0; i < S.length(); i++)
{
if((sa[i] < n) != (sa[i+1] < n))
{
res = max(res, lcp[i]);
}
}
printf("Nejdelsi spolecny retezec ma delku %d.\n", res);
}
}
可能你会觉得如果 子字符串 a 和 b( 在不同字符串中 )有最长公共字符串子串长度,这时如果有个和 b 在同一个字符串的 子字符串 c,如果 b 与 c 的最长公共字符串子串长度更长,会不会影响判断?
这个当然不会,如果在后缀数组排序中是 a b c,这自然不会影响,如果是 a c b,则 a 与 b 相同的前缀,c 一定也有相同的前缀,所以自然也不会影响