• SA学习笔记


    关于后缀排序这玩意应该都知道了.........

    看这篇博客你可能需要一点前置知识.......建议先自行了解SA的一些定义.

    梳理一下(height)数组的性质、求法以及(LCP(i,j))的求法

    约定几个前置定义:

    (Suffix(i)) :表示后缀(i)

    (SA[i]) : 表示排名第 (i) 的后缀

    (rk[i]) :表示后缀(i)的排名

    (LCP(i,j)) :表示后缀(i)和后缀(j)的最长公共前缀

    (str):表示的就是给出的字符串,(str[i]就表示第i个字符)

    (height)数组的介绍

    (height[i]表示的是排名为i的后缀与排名为i-1的后缀的最长公共前缀)

    表示出来即为 : (height[i] = LCP(SA[i],SA[i - 1]);)

    (height)数组的性质:

    • (height[i] >= height[i - 1] - 1)(可以用来求(height数组))
    • 对于(LCP(i,j)),不妨令(rk[i]) < (rk[j]),那么(LCP(i,j) = min(height[rk[i]+1],height[rk[i]+2],height[rk[i]+3]......height[rk[j]]))

    关于上面性质的证明:后缀数组 罗穗骞

    这里就不再赘述了。

    (height)数组的方法:

    定义h数组:(h[i] = height[ rk[i] ])。按照(h[1] , h[2] , h[3]....h[n]的顺序求解)

    有一点点绕,(rk[i]表示的是后缀i排序后的排名)

    (height[rk[i]])就是当前(后缀i排序后的排名 与 后缀i排序后的排名 - 1的最长公共前缀)

    我们依次遍历以(str[1])为开头的后缀,以(str[2])为开头的后缀.....以(str[3])为开头的后缀,同时求解(h[i])

    放上代码来讲吧:

    void GetHeight()
    {
        int k=0;
        for(int i = 1 ; i <= n ; i ++)
        {
            if(k)k--;
            int j = SA[rk[i]-1];//获得排名 rk[i] - 1 的后缀的位置,i则是排名rk[i]的后缀的位置
            while(str[i + k] == str[j + k])k ++;//暴力匹配
            height[rk[i]] = k;//rk[i]表示的是后缀i的排名
        }
        return ;
    }
    

    这样子就可以求得(height)数组了

    对于(LCP(i,j))的求法

    我们上面写了"对于(LCP(i,j)),不妨令(rk[i]) < (rk[j]),那么(LCP(i,j) = min(height[rk[i]+1],height[rk[i]+2],height[rk[i]+3]....height[rk[j]]))"
    于是这就是一个经典的,RMQ可以维护的区间最值问题了。
    RMQ O((nlog(n)))预处理,O(1)查询即可 

    ps.我写的这个后缀数组基本上都是写成函数的形式,方便您观看,也便于作为模板

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    struct Pre{
    	int data,id;//预处理要用的 
    } P[500005];
    
    struct Node {
    	int K1,K2,id;
    } S[500005];
    
    char a[500005];
    int SA[500005],rk[500005],height[500005];
    int Min[500005][25];//RMQ要用
    int n ;
    
    int cmpP(Pre A,Pre B);
    int cmpN(Node A, Node B);
    void Sort();//排序
    bool GetRank(int x);//排序
    void GetHeight();
    void DealRMQ();
    void prepare();
    int LCP(int l,int r);//给出的函数对应下面的顺序,方便查找 
    
    int main()
    {
    	prepare();//预处理以及读入
    	Sort();//进行后缀排序 
    	for(int i = 1 ; i <= n ; i ++)SA[rk[i]] = i;//给SA数组赋值
    	GetHeight();//获得height数组
    	DealRMQ();//处理RMQ
    	int ans = 0;
    	return 0;
    }
    
    int cmpP(Pre A,Pre B)
    {
    	return A.data < B.data;
    }
    
    int cmpN(Node A, Node B)
    {
    	if(A.K1 != B.K1)return A.K1 < B.K1;
    	else return A.K2 < B.K2;
    }
    
    void Sort()
    {
    	int x = 1;
    	while( ! GetRank(x) )x << 1;//倍增
    	return ;
    }
    
    bool GetRank(int x)
    {
    	for(int i = 1 ; i <= n ; i ++)
    	{
    		S[i].K1 = rk[i];
    		if(i + x > n)S[i].K2 = 0 ;
    		else S[i].K2 = rk[i + x];
    		S[i].id = i;
    	}
    	sort(S + 1 , S + 1 + n , cmpN);//没有写基排
    	S[0].K1 = -1 , S[0].K2 = -1;
    	int now = 0;
    	for(int i = 1 ; i <= n ; i ++)
    	{
    		if(S[i].K1 != S[i-1].K1 || S[i].K2 != S[i-1].K2)now ++;
    		rk[S[i].id] = now;
    	}
    	if(now == n)return 1;//如果排名都不相同,排名完毕,返回1
    	else return 0;//否则返回0
    }
    
    void GetHeight()
    {
    	int k = 0;
    	for(int i = 1 ; i <= n ; i ++)
    	{
    		if(k) k --;
    		int j = SA[rk[i] - 1];
    		while(a[j + k] == a[i + k]) k ++;
    		height[rk[i]] = k;
    	}
    	return ;
    }
    
    void DealRMQ()
    {
    	for(int i = 1 ; i <= n ; i ++)Min[i][0] = height[i];
    	for(int j = 1 ; j <= log2(n); j ++)
    		for(int i = 1 ; i + (1 << j) - 1 <= n ; i ++)
    	Min[i][j] =	min(Min[i][j-1],Min[i + (1 << (j-1))][j - 1]);
    }
    
    void prepare()
    {	
    	cin >> a + 1;
    	n = strlen(a + 1);
    	for(int i = 1 ; i <= n ; i ++)
    	{
    		P[i].id = i ;
    		P[i].data = a[i] - 'a' + 1;
    	}
    	sort(P + 1 , P + 1 + n , cmpP);
    	for(int i = 1 , now = 0 ; i <= n ; i ++)
    	{
    		if(P[i].data != P[i-1].data)now ++;
    		rk[P[i].id] = now;//处理一开始的排名,给rk数组赋初值
    	}
    	return ;
    }
    
    int LCP(int l,int r)
    {
    	int Les = min(rk[l],rk[r]) + 1;
    	int Gre = max(rk[l],rk[r]);
    	int k = log2(Gre - Les + 1);
    	return min(Min[Les][k],Min[Gre - (1 << k) + 1][k]);
    }
    
  • 相关阅读:
    socket 中文man页面函数
    指针和数组(上)
    char和unsigned char--数据类型区别
    自己的总结
    warning C4305:“初始化”:从“double”到“float”截断
    指针数组和数组指针区别
    Python模块常用的几种安装方式
    Jenkins环境搭建
    wxPython:事件
    wx.ListCtrl简单使用例子
  • 原文地址:https://www.cnblogs.com/MYCui/p/13894965.html
Copyright © 2020-2023  润新知