ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

后缀数组

2022-07-12 20:02:37  阅读:142  来源: 互联网

标签:后缀 基数排序 数组 Theta sa rk


后缀数组

​ 最近学习了后缀数组,第一次写标程的时候还是很痛苦的,最后还是用一种比较易懂的方法写完了标程。

声明

  • 后缀:表示从一个字符串的一个字符开始往后的字符构成的字符串
  • \(sa_i\):表示排名为\(i\)后缀的开始位置是\(S_{sa_i}\)
  • \(rk_i\):表示以\(S_{sa_i}\)为开头的后缀的排名
  • \(h_i\)是表示以\(S_{sa_i}\)和\(S_{sa_{i-1}}\)为开头的字符串的最长公共字串的长度

方法

后缀数组的求法

算法思路

我们的目标是得出\(rk\)或\(sa\)数组中任意一个就可以求出另外一个因为我们有一个很明显的性质。

\[rk[sa[i]]=sa[rk[i]]=i \]

我们有两种方法可以求出这个数组但由于本人太弱了只会用倍增法求。

2022-07-12 15-49-49屏幕截图.png

如图我们可以每一次用双关键字的基数排序便可以求出来,由于倍增是\(\Theta(\log n)\)的,每次排序是\(\Theta(n)\)的所以总的时间复杂度是\(\Theta(n\log n)\)在大步分提目是够用的了,而且\(CD3\)常数较大,有些情况下甚至不如倍增的方法。

code

//tot是基数排序的辅助数组
//eh是每次基数排序的第二关键字
//tn是每次基数排序的第一关键字
void work(){
    for(int i=0;i<n;i++)tot[r[i]]=1;
    for(int i=1;i<M;i++)tot[i]+=tot[i-1];
    for(int i=0;i<n;i++)rk[i]=tot[r[i]]-1;
    for(int l=1;l<n;l<<=1){//倍增
        for(int i=0;i<n-l;i++)eh[i]=rk[i+l]+1;
        for(int i=n-l;i<n;i++)eh[i]=0;
        for(int i=0;i<n;i++)tn[i]=rk[i]+1;//求出第一二关键字
        for(int i=0;i<=n;i++)tot[i]=0;
        for(int i=0;i<n;i++)tot[eh[i]]++;
        for(int i=1;i<=n;i++)tot[i]+=tot[i-1];
        for(int i=n-1;~i;i--)rk[i]=--tot[eh[i]];//按第二关键字排序
        for(int i=0;i<n;i++)sa[rk[i]]=i;
        for(int i=0;i<=n;i++)tot[i]=0;
        for(int i=0;i<n;i++)tot[tn[i]]++;
        for(int i=1;i<=n;i++)tot[i]+=tot[i-1];
        for(int i=n-1;~i;i--)
        rk[sa[i]]=--tot[tn[sa[i]]];//按第一关键字排序
        for(int i=0;i<n;i++)sa[rk[i]]=i;
        for(int i=1,cnt=0;i<n;i++)
        if(eh[sa[i]]==eh[sa[i-1]]&&tn[sa[i]]==tn[sa[i-1]])
        rk[sa[i]]=cnt;else rk[sa[i]]=++cnt;//去重,除最后一次没有必要去重以外每次操作完都需要去重,方便起见每次进行去重
        for(int i=0;i<n;i++)sa[rk[i]]=i;//算出每次的sa数组
    }
    return;
}

公共字串数组求法

算法思路

我们令\(h_i\)表示,以\(sa_{i-1}\)和\(sa_i\)为开头的最长公共前缀,我们有如下性质。

若\(rk_j<rk_k\)则,以\(rk_j\)和\(rk_k\)为开头的后缀的最长公共前缀长度为

\[\min_{i=rk_j+1}^{rk_k}h_i \]

我们知道\(h\)数组会有这样一个性质

\[h_{rk_i}\ge h_{rk_{i-1}}-1 \]

这样我们在\(\Theta(n)\)的时间内便可求出\(h\)数组

code

void makeh(){
    for(int i=0,k=0,j;i<n;h[rk[i++]]=k)if(rk[i])
    for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
}

标签:后缀,基数排序,数组,Theta,sa,rk
来源: https://www.cnblogs.com/dzrblog/p/16471453.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有