后缀数组模板第一版

/*---------------倍增算法+RMQ后缀数组模板--------------

输入:从0开始的字符串g,长度len最大为10^6
输出:
 sa[]表示:n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺
 次放入 sa 中,sa[i]表示排第i位的字符串开头是sa[i],因为添加了一个结尾0,所以sa[0]=len
 height 数组(h[]):定义 h[i]=suffix(sa[i-1])和 suffix(sa[i])的最长公
 共前缀,也就是排名相邻的两个后缀的最长公共前缀。
 mrank[i]表示:以i位开头的字符串,排名为mrank[i]。mrank[len]=0;
 cal(x,y):查询g[b]g[b+1]...g[len-1]与g[d]g[d+1]...g[len-1]的最大前缀数。
复杂度:空间复杂度33*len,时间复杂度,建立后缀数组为len*log(len),若不需要建立RMQ为len

--------------------------------------------------*/

int r[N];
int sa[N];
int scnt[N];
int wa[N],wb[N],wv[N];
int mrank[N];
int h[N],th[N];
int rmqdp[N][22];
int savek[N];

int cmp(int gg[],int a,int b,int k)
{
    return gg[a]==gg[b] && gg[a+k]==gg[b+k];
}

void getsa(int str[],int sa[],int n,int m)
{
    int i,*x,*y,*t;
    x=wa; y=wb;
    memset(scnt,0,sizeof(scnt));
    for(i=0;i<n;i++)
            scnt[ x[i]=str[i] ]++;
    for(i=1;i<m;i++)
            scnt[i]+=scnt[i-1];
    for(i=0;i<n;i++)
            sa[ --scnt[ str[i] ] ]=i;

    for(int p=1,j=1;p<n;j*=2,m=p)
    {
        for(p=0,i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if( sa[i]>=j ) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[ y[i] ];
        memset(scnt,0,sizeof(scnt));
        for(i=0;i<n;i++) scnt[ wv[i] ]++;
        for(i=1;i<m;i++) scnt[i]+=scnt[i-1];
        for(i=n-1;i>=0;i--) sa[ --scnt[ wv[i] ] ] = y[i];
        for(p=1,t=x,x=y,y=t,x[sa[0]]=0,i=1;i<n;i++)
            x[ sa[i] ] = cmp(y,sa[i],sa[i-1],j)?p-1:p++;
    }
}

void geth(int str[],int n)
{
    h[n-1]=0;
    int p=0;
    for(int i=0;i<n-1;i++)
    {
        int tmp=mrank[i];
        while( str[i+p] == str[ sa[tmp-1]+p ] ) p++;
        h[i]=p;
        p--;
        p=max(0,p);
    }
}

void buildst(int n)
{
    for(int i=1;i<=n;i++)
        rmqdp[i][0] = th[i];
    for(int i=1;(1<<i)<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if( j+(1<<(i-1)) >n ) rmqdp[j][i]=rmqdp[j][i-1];
            else rmqdp[j][i]=min(rmqdp[j][i-1],rmqdp[j+(1<<(i-1))][i-1]);
        }
    }
}

int cal(int x,int y)
{
        x=mrank[x]; y=mrank[y];
        if(x>y) swap(x,y);
        x++;
        //然后就是求x到y的最小值
        int k = savek[y-x+1];
        return min(rmqdp[x][k],rmqdp[y-(1<<k)+1][k]);
}

void SuffixInit(char gg[],int len)
{
    //初始化RMQ中得k
    for(int i=1;i<N;i++)
        savek[i] = floor( log((double)i)/log(2.0) );

    for(int i=0;i<len;i++)
        r[i]=gg[i];

    r[len++]=0;
    getsa(r,sa,len,300);
    for(int i=0;i<len;i++)
        mrank[ sa[i] ]=i;
    //for(int i=0;i<len;i++) printf("%s\n",g+sa[i]);
    geth(r,len);
    for(int i=0;i<len-1;i++)
        th[ mrank[i] ]= h[i];
    buildst(len-1);

    /*
     int b,d;
     while(scanf("%d%d",&b,&d))
     {
        printf("%d\n",cal( b,d ));//查询g[b]g[b+1]...g[len-1]与g[d]g[d+1]...g[len-1]的最大前缀数。
     }
     */
}
//求sa更快的3DC3
#define N 20020
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)

int wa[N],wb[N],wv[N],mws[N];
char s[N];
int str[3*N];
int sa[3*N];
int mrank[N];
int h[N];
int c0(int *r,int a,int b)
{
    return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}

int c12(int k,int *r,int a,int b)
{
    if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
    else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];
}

void msort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for(i=0;i<n;i++) wv[i]=r[a[i]];
    for(i=0;i<m;i++) mws[i]=0;
    for(i=0;i<n;i++) mws[wv[i]]++;
    for(i=1;i<m;i++) mws[i]+=mws[i-1];
    for(i=n-1;i>=0;i--) b[--mws[wv[i]]]=a[i];
    return;
}
void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    r[n]=r[n+1]=0;
    for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
    msort(r+2,wa,wb,tbc,m);
    msort(r+1,wb,wa,tbc,m);
    msort(r,wa,wb,tbc,m);
    for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
    rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
    if(p<tbc) dc3(rn,san,tbc,p);
    else for(i=0;i<tbc;i++) san[rn[i]]=i;
    for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
    if(n%3==1) wb[ta++]=n-1;
    msort(r,wb,wa,ta,m);
    for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
    for(i=0,j=0,p=0;i<ta && j<tbc;p++)
    sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
    for(;i<ta;p++) sa[p]=wa[i++];
    for(;j<tbc;p++) sa[p]=wb[j++];
    return;
}
void geth(int str[],int n)
{
    for(int i=0;i<n;i++)
        mrank[ sa[i] ]=i;
    h[n-1]=0;
    int p=0;
    for(int i=0;i<n-1;i++)
    {
        int tmp=mrank[i];
        while( str[i+p] == str[ sa[tmp-1]+p ] ) p++;
        h[i]=p;
        p--;
        p=max(0,p);
    }
}
int main()
{
    while(scanf("%s",s))
    {
        int len=strlen(s);
        int n=0;
        for(int i=0;i<len;i++)
            str[n++]=s[i];
        str[n++]=0;
        dc3(str,sa,n,256);
        geth(str,n);
        //已经得到h
    }
    return 0;
}
时间: 2024-11-03 22:10:17

后缀数组模板第一版的相关文章

后缀数组模板一份

1 /****************** 2     by zhuyuqi      * 3     QQ:1113865149 * 4     name:2-sat    * 5                   * 6 ******************/ 7  8 using namespace std; 9 const int MAX = 1000;10 int r[MAX],*rank;11 int wa[MAX],wb[MAX],ws[MAX],wv[MAX];12 int h

后缀数组模板/LCP模板

1 //后缀数组模板,MANX为数组的大小 2 //支持的操作有计算后缀数组(sa数组), 计算相邻两元素的最长公共前缀(height数组),使用get_height(); 3 //计算两个后缀a, 和b的最长公共前缀,请先使用lcp_init(),再调用get_lcp(a, b)得到 4 //下面的n是输入字符串的长度+1(n = strlen(s) + 1), m是模板的范围 m=128表示在字母,数字范围内,可以扩大也可缩小 5 //s[len] 是插入的一个比输入字符都要小的字符 6 s

后缀数组模板及解释

以前做过后缀数组,直接用模板,最近打算重新认真的学一遍.感觉学一个东西一定要弄懂了,不然到最后还是要重学. int wa[MAXN],wb[MAXN],wv[MAXN],Ws[MAXN]; void da(int *r,int *sa,int n,int m){//n表示字符串长度 + 1,包括添加的那个0,m表示取值的范围 //把单个字符进行基数排序 int *x = wa,*y = wb; for(int i = 0; i < m; i++)Ws[i] = 0; for(int i = 0;

后缀数组模板及一些数组的含义

最近学习了一下后缀数组,模板原理以后再看,先记一下一些数组的含义.用以下这张图做例子: rank(i)代表第i个后缀的字典序排名 sa(i)代表排名为i的字典序对应的位置 lcp(i, j)表示suffix(i)和suffix(j)的公共最长前缀 height(i) = lcp(sa(i-1), sa(i)) 当rank(i)<rank(j),有lcp(i,j) = min(height(k)),   rank(i)<k<=rank(j) 原文地址:https://www.cnblogs

hdu1403(后缀数组模板)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1403 题意: 给出两个字符串, 求他们的最长公共子串 思路: 两个字符串的最长公共子串长度显然就是两个字符串的所有后缀中的最长公共前缀长度. 可以先用一个没有出现的字符(便于后面区分后缀是否属于相同字符串)将两个字符串连成一个字符串,再用后缀数组求其height, SA数组, 对于当前 i, 通过 SA 数组区分后缀 i 和 i - 1 是否在同一个初始字符串中, 若不是则用 height[i] 维

后缀数组 模板题 hdu1403(最长公共(连续)子串)

好气啊,今天没有看懂后缀树和后缀自动机 只能写个后缀数组发泄一下了orz #include <cstdio> #include <cstring> const int N = 100017*2; int wa[N], wb[N], wv[N], ws[N]; int rank[N]; int height[N]; char str[N]; int s[N], sa[N]; int cmp(int *r, int a, int b, int l) { return r[a]==r[b

后缀数组模板 SuffixArray

1 #include<bits/stdc++.h> 2 #define rep(i,a,b) for(i=a;i<b;++i) 3 #define drep(i,a,b) for(i=b;i>=a;--i) 4 using namespace std; 5 int p[N],wc[N],wv[N],wa[N],wb[N],rank[N],sa[N]; 6 char s[N]; 7 inline bool cmp(int *r,int a,int b,int l) { 8 retur

后缀数组模板(理解)

字符串的处理真可谓是博大精深,后缀数组这种数据结构我花了两天时间才明白了其构造的过程.主要是代码不好理解. 数据结构: 1.sa数组,就是后缀数组,按照字典序排列,其意义为:sa[i]=k,排第i名的子串是从k位开始的. 2.rank名次数组,其意义为:rank[i]=k,以i为起点的子串排名为k. 很容易看出来两者可以相互转化. 求这两个数组的过程是基于基数排序,计数排序的方法. 下面是一个大牛的注释版.其中我在补充点: 1.对于求y[]这个数组,因为已经知道上次长为j的子串比较结果,那么这次

[UOJ#35] [UOJ后缀数组模板题] 后缀排序 [后缀数组模板]

后缀数组,解决字符串问题的有利工具,本题代码为倍增SA算法 具体解释详见2009年国家集训队论文 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cmath> 7 #include <ctime> 8 9 using namespace