您的位置:首页 > 其它

TYVJ 1860 后缀数组入门模版题

2013-10-30 18:16 288 查看
题意:。。。。。

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 200000 + 10;
const int sigma_size = 27; //擦,少了个桶子  我悲剧了30次。。。。

/*
*suffix array
*倍增算法  O(n*logn)
*待排序数组长度为n,放在0~n-1中,在最后面补一个0
*build_sa( ,n+1, );//注意是n+1;
*getHeight(,n);
*例如:
*n   = 8;
*num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
*rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank
必定为0无效值
*sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
*height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
*
*/

int sa[maxn],t1[maxn],t2[maxn],c[maxn];
int Rank[maxn],height[maxn];

void build_sa(int s[],int n,int m)
{
int i,j,p,*x=t1,*y=t2;
for(i=0;i<m;i++)  c[i]=0;
for(i=0;i<n;i++)  c[x[i]=s[i]]++;
for(i=1;i<m;i++)  c[i]+=c[i-1];
for(i=n-1;i>=0;i--)  sa[--c[x[i]]]=i;
for(j=1;j<=n;j<<=1)
{
p=0;
for(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<m;i++)  c[i]=0;
for(i=0;i<n;i++)  c[x[y[i]]]++;
for(i=1;i<m;i++)  c[i]+=c[i-1];
for(i=n-1;i>=0;i--)  sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
if(p>=n)  break;
m=p;
}
}

void getHeight(int s[],int n)
{
int i,j,k=0;
for(i=0;i<=n;i++)  Rank[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)  k--;
j=sa[Rank[i]-1];
while(s[i+k]==s[j+k])  k++;
height[Rank[i]]=k;
}
}

char str[maxn];
int S[maxn];

int main()
{
while(~scanf("%s",str)){
int n=strlen(str);
for(int i=0;i<n;i++)  S[i]=str[i]-'a'+1;
build_sa(S,n+1,sigma_size);
getHeight(S,n);
for(int i=1;i<n;i++)
printf("%d ",sa[i]+1);
printf("%d\n",sa
+1);
for(int i=1;i<n;i++)
printf("%d ",height[i]);
printf("%d\n",height
);
}
return 0;
}


另一种版本C++已经封装:

#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
/*   时间复杂度:O(nlogn)
*   注意点
*   1.insert进的全部数都必须大于0,如出现特殊情况,可根据题意将原数加上某个数使之大于0
*   2.getsa()函数中的m默认为256 m = max{ init[i] } + 1 可根据题意更改
*   3.输入的数据从下标0开始存储在init数组,第size位则手动添加上个0
*   4.询问lcp时输入a,b必须不同
*/
class SuffixArray
{
public:
static const int maxn= 200000 + 10;
int init[maxn];   //将初始数据,保存在init里,并且保证每个数字都比0大
int X[maxn];
int Y[maxn];
int Rank[maxn];   //名次数组,从0开始
int sa[maxn];     //sa从1开始,因为最后一个字符(最小的)排在第0位
int high[maxn];   //high从1开始,因为表示的是sa[i]和sa[i+1] [0,size)
int buc[maxn];
int log2[maxn];
int best[maxn][20];
int size;
//初始数据个数
void clear()
{
size=0;
}

//输入一个数
void insert(int n)
{
init[size++]=n;
}

bool cmp(int *r,int a,int b,int l)
{
return (r[a]==r[b]&&r[a+l]==r[b+l]);
}

//当需要反复询问两个后缀的最长公共前缀时用到RMQ
//n为字符串长度,m为最大值一般256
void getsa(int m=256)
{
init[size]=0;
int i,l,p,*x=X,*y=Y,n=size+1;
for(i=0;i<m;i++)
buc[i]=0;
for(i=0;i<n;i++)
buc[x[i]=init[i]]++;
for(i=1;i<m;i++)
buc[i]+=buc[i-1];
for(i=n-1;i>=0;i--)
sa[--buc[x[i]]]=i;
for(l=1,p=1;p<n;m=p,l*=2)
{
p=0;
for(i=n-l;i<n;i++)
y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=l)
y[p++]=sa[i]-l;
for(i=0;i<m;i++)
buc[i]=0;
for(i=0;i<n;i++)
buc[x[y[i]]]++;
for(i=1;i<m;i++)
buc[i]+=buc[i-1];
for(i=n-1;i>=0;i--)
sa[--buc[x[y[i]]]]=y[i];
for(swap(x,y),x[sa[0]]=0,i=1,p=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],l)?p-1:p++;
}
}

void gethigh()
{
int i,j,h=0,n=size;
for(i=1;i<=n;i++)
Rank[sa[i]]=i;
high[0]=0;
for(i=0;i<n;i++)
{
if(h>0) h--;
j=sa[Rank[i]-1];
for(;i+h<n&&j+h<n&&init[i+h]==init[j+h];h++);
high[Rank[i]-1]=h;
}
}

//预处理每个数字的Log值,常数优化,用于RMQ
void initLog()
{
int i;
log2[0]=-1;
for(i=1;i<maxn;i++)
log2[i]=(i&(i-1))?log2[i-1]:log2[i-1]+1;
}

//初始化RMQ
void initRMQ()
{
int i,j,limit,n=size;
initLog();
for(i=0;i<n;i++)
best[i][0]=high[i];
for(j=1;j<=log2
;j++)
{
limit=n-(1<<j);
for(i=0;i<=limit;i++)
best[i][j]=min(best[i][j-1],best[i+(1<<j-1)][j-1]);
}
}

//询问a,b后缀的最长公共前缀 用lcp之前,先初始化RMQ
int lcp(int a,int b)
{
int t;
a=Rank[a];
b=Rank[b];
if(a>b)
swap(a,b);
b--;
t=log2[b-a+1];
return min(best[a][t],best[b-(1<<t)+1][t]);
}
}SA;

char s[200010];

int main()
{
int i,len;
while(~scanf("%s",s))
{
SA.clear();
len=strlen(s);
for(i=0;i<len;i++)
SA.insert(s[i]-'a'+1);
SA.getsa();
SA.gethigh();
printf("%d",SA.sa[1]+1);
for(i=2;i<=len;i++)
printf(" %d",SA.sa[i]+1);
printf("\n");
printf("%d",SA.high[0]);
for(i=1;i<len;i++)
printf(" %d",SA.high[i]);
printf("\n");
}
return 0;
}


第一种效率最快哦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: