码迷,mamicode.com
首页 > 编程语言 > 详细

并不对劲的后缀数组

时间:2018-01-19 11:40:36      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:strlen   include   .com   algorithm   快速   +=   倍增   hid   是什么   

 

后缀数组sa(x)表示排序后第x位在排序前的位置。

这个东西的求法有两种,一种是倍增,时间复杂度o(n log n)或o(n log2n),另一种是用不知道什么方法做到的o(n)。

至于第二种方法是什么,并不对劲的人并不知道,所以只说倍增。

考虑正常地比较两个字符串,都是从头比较到尾:

 技术分享图片

那么,如果把两个字符串都断成两半,并且已知每一段的排名,就相当于以第一段为第一关键字,第二段为第二关键字排序了。

技术分享图片

根据这个性质,就能想到如果先把字符串的每个位置开始长度为一的子串进行排序后,就能在至多n log n的时间内将每个位置开始长度为二的子串排序。

技术分享图片

↑大概长这样,注意最后要补一个空字符。

以此类推,就能这样倍增地求出后缀的排序了,还是要注意最后补空字符。

如果用基数排序,每次排序的时间复杂度是o(n)的,那么总复杂度就是o(n log n)了。

如果用快速排序,总复杂度就是o(n log2n),心中有党常数极小才能过。

技术分享图片
#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define maxn 2000010
using namespace std;
inline int read()
{
    int xx=0,ff=1;
    char ch=getchar();
    while(isdigit(ch)==0&&ch!=-)ch=getchar();
    if(ch==-)ff=-1,ch=getchar();
    while(isdigit(ch))xx=xx*10+ch-0,ch=getchar();
    return xx*ff;
}
void write(int x)
{
    int ff=0;char ch[15];
    if(x<0)
    {
        x=-x;
        putchar(-);
    }
    while(x)ch[++ff]=(x%10)+0,x/=10;
    if(ff==0)putchar(0);
    while(ff)putchar(ch[ff--]);
    putchar( );
}
struct SA
{
    int sa[maxn],ord[maxn],x[maxn],n,m; 
    int y[maxn],c[maxn];
    char s[maxn];
    void start()
    {
        scanf("%s",s);
        n=strlen(s);m=130;
    }
    void s_sort() 
    {
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        memset(c,0,sizeof(c));
        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;
        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];
            swap(x,y);
            p = 1;
            x[sa[0]] = 0;
            for (int i=1;i<n;i++)
                x[sa[i]]= (y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++;
            if (p>=n) break;
            m=p;
        }
    }
    void print()
    {
        for(int i=0;i<n;i++)
             write(sa[i]+1);
    }
}t;
int main()
{
    t.start();
    t.s_sort();
    t.print();
    return 0;
}
并不需要常数优化的
技术分享图片
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define maxn 1000010 
using namespace std;
int read()
{
    int ff=1,x=0;char ch=getchar();
    while(isdigit(ch)==0 && ch!=-)ch=getchar();
    if(ch==-)ff=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-0,ch=getchar();
    return x*ff;
}
void write(int x)
{
    int ff=0;char ch[15];
    while(x)ch[++ff]=(x%10)+0,x/=10;
    while(ff)putchar(ch[ff--]);
    putchar( );
} 
char s[maxn];
bool f[maxn];
int rnk[maxn*2],ord[maxn*2],tp[maxn*2],n,c;//tmp(x)排在x位的是几号数,ord(x)第x号数排在第几位 
void init()
{
    scanf("%s",s);
    n=strlen(s);
    for(int i=1;i<=n;i++)
        ord[i]=int(s[i-1]),rnk[i]=i;
    for(int i=n+1;i<=n*2;i++)
        ord[i]=0;
}
bool cmp(int a,int b)
{
    return ord[a]==ord[b]?ord[a+c]<ord[b+c]:ord[a]<ord[b];
}
void print()
{
    for(int i=1;i<=n;i++)
    {
        write(rnk[i]);
    }
    printf("\n"); 
}
void cpy()
{
    for(int i=1;i<=n;i++)
    {
        ord[i]=tp[i];
    }
}
void s_sort()
{
    int yes=0;c=0;
    do
    {
        sort(rnk+1,rnk+n+1,cmp);int tmp=1;
       // print();
        for(int i=1;i<=n;i++)
        {
            tp[rnk[i]]=tmp;yes=tmp;
            tmp+=(ord[rnk[i]]==ord[rnk[i+1]]&&ord[rnk[i]+c]==ord[rnk[i+1]+c])?0:1;
        }
        cpy();
        if(c!=0)c*=2;
        else c=1;
    }while(yes<n) ;
} 
int main()
{
    init();
    s_sort();
    print();
    return 0;
}
不常数优化会t的

 

并不对劲的后缀数组

标签:strlen   include   .com   algorithm   快速   +=   倍增   hid   是什么   

原文地址:https://www.cnblogs.com/xzyf/p/8244506.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!