题意:求两个长度最长是100000的字符串的公共子串。
思路:参考罗穗骞的论文,就是在两个字符串中间加一个字符,利用倍增算法求height数组,height数组存储了排名相邻的两个后缀的最长公共子串长度,所以枚举新形成的字符串的height,同时要保证相邻两个后缀字符串是分别属于这两个字符串的。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 200010;
int num[maxn];
int sa[maxn] , rank[maxn] , height[maxn];
int wa[maxn] , wb[maxn] , wv[maxn] , wd[maxn];
int cmp(int *r , int a , int b , int l) {
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int *r , int n , int m) { //r是待匹配的数组 , n是字符串总长度 , m是字符范围
int i , j , p;
int *x = wa , *y = wb , *t;
for(i = 0 ; i < m ; i ++) wd[i] = 0;
for(i = 0 ; i < n ; i ++) wd[x[i]=r[i]] ++;
for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1];
for(i = n-1 ; i >= 0 ; i --) sa[-- wd[x[i]]] = i;/*****基排*****/
for(j = 1 , p = 1 ; p < n ; j*=2 , m = p) {
for(p = 0 , i = n - j ; i < n ; i ++) y[p++] = i;
for(i = 0 ; i < n ; i ++) if(sa[i] >= j) y[p++] = sa[i] - j;/*****第二关键字排序*****/
for(i = 0 ; i < n ; i ++) wv[i] = x[y[i]];
for(i = 0 ; i < m ; i ++) wd[i] = 0;
for(i = 0 ; i < n ; i ++) wd[wv[i]] ++;
for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1];
for(i = n - 1 ; i >= 0 ; i --) sa[-- wd[wv[i]]] = y[i];/*****第一关键字排序*****/
for(t = x , x = y , y = t , p = 1 , x[sa[0]] = 0 , i = 1 ; i < n ; i ++) {
x[sa[i]] = cmp(y , sa[i-1] , sa[i] , j) ? p - 1: p ++;
}
}
}
void calheight(int *r , int n) { //求height数组
int i , j , k = 0;
for(i = 1 ; i <= n ; i ++) rank[sa[i]] = i;
for(i = 0 ; i < n ; height[rank[i++]] = k) {
for(k ? k -- : 0 , j = sa[rank[i]-1] ; r[i+k] == r[j+k] ; k ++);
}
}
int main() {
char str[maxn];
int len1 , len2 , i , m , n , ans;
while(scanf("%s",str)!=EOF) {
len1 = strlen(str);
for(i = 0 ; i < len1 ; i ++)
num[i] = str[i] - 'a' + 2;
num[len1] = ' ';
scanf("%s",str);
len2 = strlen(str);
for(i = len1+1 ; i <= len1+len2 ; i ++)
num[i] = str[i - (len1+1)] - 'a' + 2;
n = len1+len2+1;
num[n] = 0; //给这个位置赋个值 , 去掉也能过。。。
m = 50;
ans = 0;
for(i = 0 ; i < n ; i ++)
printf("%d ",num[i]);
da(num , n+1 , m);//因为函数中是"< n" 所以n应该等于len1+len2+2
calheight(num , n);
for(i = 2 ; i <= n ; i ++) {
// printf("%d\n",height[i]);
if((sa[i]>len1&&sa[i-1]<len1)||(sa[i]<len1&&sa[i-1]>len1))
ans = max(ans , height[i]);
}
printf("%d\n",ans);
}
}