后缀数组suffix array

倍增算法,时间复杂度O(nlogn)

sa从小到大保存相对大小的下标

理解LSD,x数组,sa数组

char s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn],n;

void build_sa(int m)
{
    //LSD基数排序
    int *x=t,*y=t2;//x数组保存rank
    //字串长度为1,即对每一个元素的大小排序
    for(int i=0;i<m;++i) c[i]=0;//计数数组清空
    for(int i=0;i<n;++i) c[x[i]=s[i]]++;//统计出现次数
    for(int i=1;i<m;++i) c[i]+=c[i-1];//计算前缀和
    for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i;//sa从小到大保存每一个元素的下标

    for(int k=1;k<=n;k<<=1){//k为要排序的子串长

        //排序第二keyword
        int p=0;               //y[]从小到大保存第二keyword的下标
        for(int i=n-k;i<n;++i) y[p++]=i;//从第n-k位開始的字串,第二keyword为0
        for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
                                //仅仅有下标大于k的第sa[i]个字符串的rank才干作为下一行的第sa[i]-k个字符串的第二keyword

        //排序第一keyword
                //x[y[i]]是引用第一keyword,依据LSD第二次排序要在第一次的基础上
        for(int i=0;i<m;++i) c[i]=0;//计数数组清空
        for(int i=0;i<n;++i) c[x[y[i]]]++;//统计rank出现次数
        for(int i=1;i<m;++i) c[i]+=c[i-1];//求前缀和
        for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];//sa[]从小到大保存双keyword的下标

        p=1;swap(x,y);x[sa[0]]=0;//交换x,y数组 x[]数组从0到n-1保存rank值(0到p)
        for(int i=1;i<n;++i){
            x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k] ? p-1:p++;//注意p-1
            //因为p是计数rank值不同的字符串的数量,因此双keyword同样的串视为一样的rank
        }

        if(p>=n) break; //p个字符串的rank值都不同 ,p>=n时说明大小确立,以后即使倍增,sa也不会改变
        m=p;//用来下次基数排序的最大值
    }
}

————————————————————————————————————--————————

————————————————————————————————————————————

void build_sa()
{
    int *x=t,*y=t2;
    for(int i=0;i<m;++i) c[i]=0;
    for(int i=0;i<n;++i) c[x[i]=y[i]]++;
    for(int i=1;i<m;++i) c[i]+=c[i-1];
    for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i;

    for(int k=1;k<=n;k<<=1){
        int p=0;
        for(int i=n-k;i<n;++i) y[p++]=i;
        for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;

        for(int i=0;i<m;++i) c[i]=0;
        for(int i=0;i<n;++i) c[x[y[i]]]++;
        for(int i=1;i<m;++i) c[i]+=c[i-1];
        for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];

        int p=0;swap(x,y);x[sa[0]]=0;
        for(int i=1;i<n;++i){
            x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k] p-1:p++;
        }
        if(p>=n) break;
        m=p;

    }
}
int m;
int cmp_suffix(char *pattern,int p)
{
    return strncmp(pattern,s+sa[p],m);
}

int find(char *p)
{
    m=strlen(p);
    if(cmp_suffix(p,0)<0) return -1;
    if(cmp_suffix(p,n-1)>0) return -1;
    int l=0,r=n-1;
    while(l<=r){
        int mid=l+(r-l)/2;
        int res=cmp_suffix(p,mid);
        if(!res) return mid;
        if(res>0) l=mid+1;
        if(res<0) r=mid-1;
    }
    return -1;
}

/*
设suffix(k)是排在suffix(i-1)前一名的后缀。则它们的最长公共前缀是h[i-1]

。那么suffix(k+1)将排在suffix(i)的前面(这里要求h[i-1]>1,假设h[i-1]≤

1,原式显然成立)而且suffix(k+1)和suffix(i)的最长公共前缀是h[i-1]-1,

所以suffix(i)和在它前一名的后缀的最长公共前缀至少是h[i-1]-1。依照h[1]

,h[2],……,h[n]的顺序计算。并利用h数组的性质,时间复杂度能够降为O

(n)。

*/
void get_height()
{
    for(int i=0;i<n;++i) rank[sa[i]]=i;
    int k=0;
    for(int i=0;i<n;++i){
        if(k) k--;
        int j=sa[rank[i]]-1;
        while(s[j+k]==s[i+k]) k++;
        height[rank[i]]=k;
    }
}
时间: 2024-10-11 04:13:38

后缀数组suffix array的相关文章

后缀数组(suffix array)

参考: Suffix array - Wiki 后缀数组(suffix array)详解 6.3   Suffix Arrays - 算法红宝书 Suffix Array 后缀数组 基本概念 应用:字符串处理.生物信息序列处理 后缀:学过英语的都知道什么叫后缀,就是从某个位置开始到字符串结尾的特殊子串,记住 Suffix(i)=S[i...len(S)-1],i就是后缀起始位置 后缀数组:就是将后缀排序好后放到一个一维数组里,SA[i]存放排名第i大的后缀首字符下标,并且保证 Suffix(SA

后缀数组(suffix array)详解

后缀数组(suffix array)详解 转载请注明:http://www.cnblogs.com/acmer-jsb/p/3988683.html 一.What  Is  Suffix Array? 用我的理解,后缀数组是一种功能强大的字符串处理工具,堪称字符串处理神奇,尤其是在字符串匹配方面更是有着出色的处理能力. 其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多.可以说,在信息学竞赛中后缀

利用后缀数组(suffix array)求最长公共子串(longest common substring)

摘要:本文讨论了最长公共子串的的相关算法的时间复杂度,然后在后缀数组的基础上提出了一个时间复杂度为o(n^2*logn),空间复杂度为o(n)的算法.该算法虽然不及动态规划和后缀树算法的复杂度低,但其重要的优势在于可以编码简单,代码易于理解,适合快速实现. 首先,来说明一下,LCS通常指的是公共最长子序列(Longest Common Subsequence,名称来源参见<算法导论>原书第3版p223),而不是公共最长子串(也称为最长公共子串). 最长公共子串问题是在文本串.模式串中寻找共有的

学渣乱搞系列之后缀数组

学渣乱搞系列之后缀数组 by 狂徒归来 后缀数组,其nlogn的构造方法,比较麻烦,十几个循环,基数排序?计数排序?各种排序,各种凌乱,学渣表示鸭梨很大啊!学渣从<挑战程序设计竞赛>中偷学了一点nlog2n的构造方法. 字符串后缀(Suffix)是指从字符串的某个位置开始到其末尾的字符串子串.我们认为原串和空串也是后缀. 后缀数组(Suffix Array)指的是将某个字符的所有后缀按字典序排序后得到的数组.排序方式很多,时间复杂度也不同.有基数排序的倍增法o(nlogn),有DC3构造方法o

字符串匹配(三)----后缀数组算法

一.什么是后缀数组: 字符串后缀Suffix 指的是从字符串的某个位置开始到其末尾的字符串子串.后缀数组 Suffix Array(sa) 指的是将某个字符串的所有后缀按字典序排序之后得到的数组,不过数组中不直接保存所有的后缀子串,只要记录后缀的起始下标就好了. 比如下面在下面这张图中,sa[8] = 7,表示在字典序中排第9的是起始下标为7的后缀子串,这里还有一个比较重要的数组rank,rank[i] : sa[i]在所有后缀中的排名 ,比如rk[5]=0,表示后缀下标为5的子串在后缀数组中排

后缀数组(Suffix Array)模板及简析——Part 1:构建SA和rank数组

后缀数组(Suffix Array,SA)是处理字符串的有力工具.它比后缀树更易实现,占用空间更少,并且同样可以解决千变万化的字符串问题 首先推荐罗穗骞的论文(网上搜一下就能搜到),里面对后缀数组的定义.实现和应用都做了详细的阐述 然而不幸的是罗神犇的代码简直魔性,蒟蒻表示这代码压的根本看不懂啊…… 所以在理解了后缀数组的构建过程之后,我重新写了一份模板代码.虽然啰嗦了点(代码比较大,而且变量名故意拉长了),不过相对比较好懂 而且论文中用到的辅助空间是4N,我的模板用了3N,事实上还可以优化到只

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写

hdu 4416 Good Article Good sentence (后缀数组)

题目大意: 给出一个A串和很多个B串,求出A中有多少个子串,是所有的B中没有出现的. 思路分析: 后缀数组的作用很容易的求出来整个串中不同的子串个数. 现在要求的是A中不同的,且在B中没有出现过的. 先把AB 串全部连接,跑一遍suffix array.然后求出有多少个不同的子串. 然后再单独用B 串跑 suffix array.再求出单独在B 中有多少个不同的 子串. 然后结果就是 ans1 - ans2 ... 需要注意的问题就是,连接的时候需要把每一个串后面加一个特殊符.但是求不同串的时候

hdu4416 Good Article Good sentence (后缀数组)

题意:问a串中有多少种字符串集合B中没有的连续子串. a的长度10^5,B中的总长度为不超过10^5. 解法:后缀数组题目:后缀数组能够非常easy算出来一个串中有多少种子串. 把a和B集合连起来.求一次不同子串数量,然后减掉B相互连起来的数量. 在求时候,要减掉含有链接符的子串,方法是扫一遍,枚举最后出现的连接符. 代码: /****************************************************** * @author:xiefubao ***********