题目链接:https://vjudge.net/contest/363453#problem/I
http://codeforces.com/problemset/problem/427/D
题意:给两个字符串s1和s2,找出s1和s2中的最短公共子串,且这个最短子串只在s1和s2中各出现一次。
解题思路:利用后缀数组的height数组来找后缀的前缀,先将s1和s2串合并在一起,存入r数组,然后将s1与s2合并后的总字符串进行后缀数组求出height数组。
我们可以知道height[i]表示排在在i与i-1的后缀的公共前缀,如果两个后缀串基本相同,那么他们排名一定在临近的位置。
几个关键点:
①如果要满足唯一性,即只出现一次,现在循环到i,首先要满足排名i-1与i的两个后缀串分别在不同的两个串中。
②拿排名i-1,i,i+1为例,如果想要唯一,那么要求height[i]>height[i-1]&&height[i]>height[i+1],因为取前面三者的最小值为min,那么排名为i-2,i-1,i,i+1都有公共前缀部分min,ans=min(ans,max(height[i-1]+height[i+1])+1),因为唯一性,且height[i]>height[i-1]&&height[i]>height[i+1],但i可能比他们多1多2多3多很多,但我们要求最小串,只需要在保证唯一的前提下,找最小串。比如,height[i-1]公共前缀为ab,height[i]公共前缀为abcdef,height[i+1]的公共前缀为abcd,我们要保证最短唯一性,取abcde即可,不需要再去abcdef了,所以ans=min(ans,max(height[i-1]+height[i+1])+1)
③注意两个串的分割,r[len1]=1,r[n]=0,其中n的长度取len1+len2+1
前半部分都是kuangbin的模版
const int MAXN = 20010;
int t1[MAXN], t2[MAXN], c[MAXN];//求 SA 数组需要的中间变量,不需要赋值
//待排序的字符串放在 s 数组中,从 s[0] 到 s[n-1], 长度为 n, 且最大值小于 m,
//除 s[n-1] 外的所有 s[i] 都大于 0, r[n-1]=0
//函数结束以后结果放在 sa 数组中
bool cmp(int* r, int a, int b, int l) {
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int str[], int sa[], int rank[], int height[], int n, int m) {
n++;
int i, j, p, * x = t1, * y = t2;
//第一轮基数排序,如果 s 的最大值很大,可改为快速排序
for (i = 0; i < m; i++)c[i] = 0;
for (i = 0; i < n; i++)c[x[i] = str[i]]++;
for (i = 1; i < m; i++)c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
for (j = 1; j <= n; j <<= 1) {
p = 0;
//直接利用 sa 数组排序第二关键字
for (i = n - j; i < n; i++)y[p++] = i;//后面的 j 个数第二关键字为空的最小
for (i = 0; i < n; i++)if (sa[i] >= j)y[p++] = sa[i] - j;
//这样数组 y 保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for (i = 0; i < m; i++)c[i] = 0;
for (i = 0; i < n; i++)c[x[y[i]]]++;
for (i = 1; i < m; i++)c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)sa[--c[x[y[i]]]] = y[i];
//根据 sa 和 x 数组计算新的 x 数组
swap(x, y);
p = 1; x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
if (p >= n)break;
m = p;//下次基数排序的最大值
}
int k = 0;
n--;
for (i = 0; i <= n; i++)rank[sa[i]] = i;
for (i = 0; i < n; i++) {
if (k)k--;
j = sa[rank[i] - 1];
while (str[i + k] == str[j + k])k++;
height[rank[i]] = k;
}
}
int Rank[MAXN], height[MAXN], sa[MAXN];
char s[MAXN]; //待求字符串s
int r[MAXN];
代码部分:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int MAXN = 20010;
int t1[MAXN], t2[MAXN], c[MAXN];//求 SA 数组需要的中间变量,不需要赋值
//待排序的字符串放在 s 数组中,从 s[0] 到 s[n-1], 长度为 n, 且最大值小于 m,
//除 s[n-1] 外的所有 s[i] 都大于 0, r[n-1]=0
//函数结束以后结果放在 sa 数组中
bool cmp(int* r, int a, int b, int l) {
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int str[], int sa[], int rank[], int height[], int n, int m) {
n++;
int i, j, p, * x = t1, * y = t2;
//第一轮基数排序,如果 s 的最大值很大,可改为快速排序
for (i = 0; i < m; i++)c[i] = 0;
for (i = 0; i < n; i++)c[x[i] = str[i]]++;
for (i = 1; i < m; i++)c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
for (j = 1; j <= n; j <<= 1) {
p = 0;
//直接利用 sa 数组排序第二关键字
for (i = n - j; i < n; i++)y[p++] = i;//后面的 j 个数第二关键字为空的最小
for (i = 0; i < n; i++)if (sa[i] >= j)y[p++] = sa[i] - j;
//这样数组 y 保存的就是按照第二关键字排序的结果
//基数排序第一关键字
for (i = 0; i < m; i++)c[i] = 0;
for (i = 0; i < n; i++)c[x[y[i]]]++;
for (i = 1; i < m; i++)c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)sa[--c[x[y[i]]]] = y[i];
//根据 sa 和 x 数组计算新的 x 数组
swap(x, y);
p = 1; x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
if (p >= n)break;
m = p;//下次基数排序的最大值
}
int k = 0;
n--;
for (i = 0; i <= n; i++)rank[sa[i]] = i;
for (i = 0; i < n; i++) {
if (k)k--;
j = sa[rank[i] - 1];
while (str[i + k] == str[j + k])k++;
height[rank[i]] = k;
}
}
int Rank[MAXN], height[MAXN], sa[MAXN];
char s1[MAXN], s2[MAXN];
int r[MAXN];
int main()
{
scanf("%s%s",s1,s2);
int len1=strlen(s1);
int len2=strlen(s2);
int n=len1+len2+1;
int m=0;
for(int i=0;i<len1;i++)
r[i]=s1[i];
r[len1]=1;
for(int i=0;i<len2;i++)
r[i+len1+1]=s2[i];
r[n]=0;
da(r,sa,Rank,height,n,128);
height[n+1]=0;
int ans=n;
for(int i=2;i<=n;i++)
{
int minpos=min(sa[i],sa[i-1]);
int maxpos=max(sa[i],sa[i-1]);
if(minpos<len1&&maxpos>len1)
{
if(height[i]>height[i+1]&&height[i]>height[i-1])
ans=min(max(height[i-1],height[i+1])+1,ans);
}
}
ans=(ans==n)?-1:ans;
cout<<ans<<endl;
return 0;
}