求2個字符串最長公共字串。
先合併2個字符串
一定是Height[i],且sa[i],sa[i-1]分別在2個字符串的位置,就一定是公共字串。
爲什麼呢?
首先我們先合併,(注意:在第一個字符串後面加一個不再範圍內的字符,隔開2個字符,這樣兩個字符串的最長公共字串就轉化成了新的字符串後綴的最長公共前綴(滿足2個後綴分別在2個字符串原來的位置)。我看很多人沒加分隔符,那這組數據就能hack: a aa 要麼是題意說了第二個字符串更小,要麼就是數據水。。。。。。)
任意2個後綴的最長公共前綴不好搞,但是有Height[i]這個神奇的數組。
Height[i]表示:排名i的後綴和排名i-1的後綴的最長公共前綴。那麼如果Height[k]且sa[k]在前n個,sa[k-1]在後m個hu或者相反,(即在2個字符串原來的位置),那Height一定是最優解集合。遍歷所有Height一定包含最優解。
因爲我們求得是最長公共字串,既然要最長,他們的字典序排名一定是相鄰,可以自己證明下,如果不相鄰,一定有相鄰的2個分別在2個字符串的後綴,那他們的解肯定更優。(因爲所有後綴中開頭位置只包含在前n,和後m兩中情況)。所以直接搞就行了。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
const int MAXN = 3e5 + 10;
using namespace std;
char s[MAXN],s2[MAXN];
int N, M, rak[MAXN], sa[MAXN], tax[MAXN], tp[MAXN],Height[MAXN];
//tp[i] 第二關鍵字排名i的下標
//rak[i] 第一關鍵字排名i的下標。。最終是排名i的後綴的下標
//sa[i] 下標i的後綴的排名
//Height[i] suffix(sa[i-1]) 和 suffix(sa[i]) 的最長公共前綴長度
// suffix(i)開頭下標爲i的後綴
//我們定義LCP(i,j)爲suff(sa[i])與suff(sa[j])的最長公共前綴
//LCP(i,k)=min(LCP(i,j),LCP(j,k)) 對於任意1<=i<=j<=k<=n
//LCP(i,k)=min(LCP(j,j-1)) 對於任意1<i<=j<=k<=n
/*
兩個後綴的最大公共前綴
lcp(x,y)=min(heigh[x--y]) 用rmq維護,O(1)查詢
可重疊最長重複子串
Height數組裏的最大值
不可重疊最長重複子串 POJ1743
首先二分答案xx,對height數組進行分組,保證每一組的min heightminheight都>=x>=x
依次枚舉每一組,記錄下最大和最小長度,多sa[mx]?sa[mi]>=xsa[mx]?sa[mi]>=x那麼可以更新答案
本質不同的子串的數量
枚舉每一個後綴,第ii個後綴對答案的貢獻爲len?sa[i]+1?height[i]
*/
void Qsort() {
for (int i = 0; i <= M; i++) tax[i] = 0;
for (int i = 1; i <= N; i++) tax[rak[i]]++;
for (int i = 1; i <= M; i++) tax[i] += tax[i - 1];
for (int i = N; i >= 1; i--) sa[ tax[rak[tp[i]]]-- ] = tp[i];
//基數排序,sa[tax[z]--]=tp[i],更新排名
//z是第二關鍵字排名i的後綴 放在什麼位置。 tax[z]是第一關鍵字排名一樣的放在一起
}
void SuffixSort() {
M = 122;
for (int i = 1; i <= N; i++) rak[i] = s[i] - '0' + 1, tp[i] = i;
Qsort();
for (int w = 1, p = 0; p < N; M = p, w <<= 1) {
p = 0;//這裏的p僅僅是一個計數器000
for (int i = N; i >= N-w+1; i--) tp[++p] = i;
for (int i = 1; i <= N; i++) if (sa[i] > w) tp[++p] = sa[i] - w;
Qsort();
swap(tp, rak);
rak[sa[1]] = p = 1;
for (int i = 2; i <= N; i++)
rak[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
}
//for (int i = 1; i <= N; i++)
//printf("%d ", sa[i]);
}
void GetHeight() {
int j, k = 0;
for(int i = 1; i <= N; i++) {
if(k) k--;
int j = sa[rak[i] - 1];
while(s[i + k] == s[j + k]) k++;
Height[rak[i]] = k;
//printf("%d\n", k);
}
}
int main()
{
scanf("%s%s", s + 1,s2+1);
int n = strlen(s + 1);
int m=strlen(s2+1);
// if(m>n)m=n;
s[n+1]='z'+1;
for(int i=n+2;i<=n+m+1;i++)
s[i]=s2[i-n-1];
N=n+m+1;
// cout<<s+1<<endl;
SuffixSort();
GetHeight();
//Height[i] suffix(sa[i-1]) 和 suffix(sa[i]) 的最長公共前綴長度
int mx=0;
for(int i=2;i<=N;i++)
{
if(Height[i]>mx)
{
if(sa[i]>n&&sa[i-1]<=n)
mx=Height[i];
if(sa[i]<=n&&sa[i-1]>n)
mx=Height[i];
}
}
printf("%d\n",mx);
return 0;
}