⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 suffix array.txt

📁 后缀数组的一个C++实现
💻 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 + -