📄 suffix array.txt
字号:
#include <cstdio>
#include <iostream>
using namespace std;
typedef unsigned char uchar;
void CreateSuffixArray(uchar* szText,
int L, int** _S, int** _R, int** _T1, int** _T2)
{
int i, h, h2, *T, *S1, *S2, *R, *B;
S1 = *_S; // h阶后缀数组
S2 = *_T1; // 2h阶后缀数组
R = *_R; // h阶Rank数组
B = *_T2; // 某个桶空余空间尾部的索引,兼任2h阶Rank数组
// 花O(n)的时间对h = 1进行计数排序
for(i = 0; i < 256; i++)
B[i] = 0;
for(i = 0; i < L; i++)
B[szText[i]]++;
for(i = 1; i < 256; i++)
B[i] += B[i - 1];
for(i = 0; i < L; i++)
S1[--B[szText[i]]] = i;
// 计算Rank(1),因为仅仅是1阶的Rank,所有有并列的
for(R[S1[0]] = 0, i = 1; i < L; i++)
{
if(szText[S1[i]] == szText[S1[i - 1]])
R[S1[i]] = R[S1[i - 1]];
else
R[S1[i]] = R[S1[i - 1]] + 1;
}
// log(n)趟O(n)的倍增排序
// SA(h) => Rank(h) => SA(2h) => Rank(2h) => ...
for(h = 1; h < L && R[S1[L - 1]] < L - 1; h <<= 1)
{
// 计算Rank(h)相同的后缀形成的h桶尾部的索引
// 即有多少个后缀的h前缀相同,它们被放在一个桶中
for(i = 0; i < L; i++)
B[R[S1[i]]] = i;
// 求SA(2h)
// 在同一个h桶中,所有的后缀的h前缀肯定相同,
// 那么比较他们的2h前缀,只要比较其2h前缀后半的
// 长度为h的串即可,而这个串恰恰是后面某个后缀的
// h前缀,所以我们逆向遍历有序的SA(h),
// 将S1[i] - h号前缀放到它所在桶的最后一个空位置,
// 同时,桶尾前进一个位置,这样即形成了2h桶排序
for(i = L - 1; i >= 0; i--)
if(h <= S1[i])
S2[B[R[S1[i] - h]]--] = S1[i] - h;
// 对于长度不超过h的最后几个后缀,由于在h阶段
// 它们每个实际上都已经独立分桶了(长度为h的也是)
// 而且他们的桶中有且仅有一个元素,
// 所以只要直接复制他们h阶段的SA值就可以了
// 同时,由于采用滚动数组,所以S2中“残留”了
// h/2个有效的数据,所以最终我们只需复制h/2个数据
for(i = L - h, h2 = L - (h >> 1); i < h2; i++)
S2[B[R[i]]] = i;
T = S1; S1 = S2; S2 = T;
// 计算Rank(2h)
// 2h阶段是否要分桶只需看相邻两个2h前缀前后两半
// h前缀是否全部h阶相等
for(B[S1[0]] = 0, i = 1; i < L; i++)
{
// 这里不用考虑S1[i] + h会越界
// 如果i达到了S1[i] + h越界的数值,
// 那么前面一个条件显然不会满足了
// 因为此时i前缀肯定已经独立分桶了
if(R[S1[i]] != R[S1[i - 1]] ||
R[S1[i] + h] != R[S1[i - 1] + h])
{
B[S1[i]] = B[S1[i - 1]] + 1;
}
else
B[S1[i]] = B[S1[i - 1]];
}
T = B; B = R; R = T;
}
if(*_S != S1)
*_S = S1, *_T1 = S2;
if(*_R != R)
*_R = R, *_T2 = B;
}
void CalculateHeight(int n,uchar* s,int *sa,int *height, int *rank)
{
int a,i,j,*h=new int[n];
for(i=-1;++i<n;h[i]=j){
if(!rank[i]) j=0;
else if(!i||h[i-1]<=1) for(a=sa[rank[i]-1],j=0;s[i+j]==s[a+j];++j);
else for(a=sa[rank[i]-1],j=h[i-1]-1;s[i+j]==s[a+j];++j);
}
for(i=-1;++i<n;height[i]=h[sa[i]]);
delete [] h;
}
char C[200002];
int D[4][200001];
int main()
{
int i, l1, l2, b;
int *S, *R, *H, *T;
gets(C);
l1 = (int)strlen(C);
C[l1] = '$';
gets((char*)C + l1 + 1);
l2 = l1 + 1 + (int)strlen(C + l1 + 1);
S = D[0]; R = D[1];
H = D[2]; T = D[3];
CreateSuffixArray((uchar*)C, l2, &S, &R, &H, &T);
/*for (i = 0; i < l2; ++i)
printf("S[%d] = %d ;",i, S[i]);
printf("----------------------------\n");
for (i = 0; i < l2; ++i)
printf("R[%d] = %d ;",i, R[i]);
printf("----------------------------\n");*/
//memset(H, 0, sizeof(H));
// CalculateHeight((uchar*)C, l2, S, R, H, T);
CalculateHeight(l2, (uchar*)C, S, H, R);
/*for (i = 1; i < l2; ++i)
printf("H[%d] = %d ;",i, H[i]);
printf("----------------------------\n");*/
// 求两个串的最长公共子串,只要让两个串s1、s2
// 连接在一起形成一个新串,求出新串的SA、Rank和Height
// 很显然,最长公共子串肯定出现在后缀数组某相邻两项之中
// 根据Height的定义,扫描一遍Height数组,找相邻两个分别开始于
// s1和s2串某个位置的后缀,求出所有满足这个条件的最大Height即可
for(b = 0, i = 1; i < l2; i++)
{
if(S[i] < l1 && S[i - 1] > l1 ||
S[i] > l1 && S[i - 1] < l1)
{
if(H[i] > b)
b = H[i];
}
}
printf("%d\n", b);
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -