首页 > 代码库 > 后缀数组之倍增算法
后缀数组之倍增算法
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define MAXN 123123
char s[MAXN];
int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n;
void build(int m)
{
int i,*x=t,*y=t2;
//其实下面的是计数排序
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=s[i]]++;
for(i=0;i<m;i++) c[i]+=c[i-1];//其实这个时候每个数的名次已经有了
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
/*最后一个for循环可能感觉莫名其妙可以改成下面两个for循环可能就很容易的看出来了
for(i=n-1;i>=0;i--)
rank[i]=--c[x[x[i]]];
for(i=n-1;i>=0;i--)
sa[rank[i]]=i;
根据sa[rank[i]]=i这个定理把rank[i]为--c[x[x[i]]]所以把--c[x[x[i]]]带入第二个
for循环就化为了上面计数排序的最后一个for循环
*/
for(int k=1;k<=n;k<<=1/*和k=k*2等价*/)
{
int p=0;
/*直接利用sa数组排序第二关键字也就是相当于一个两位数的个位数,y保存的是第二关键字
的sa[i]的值
*/
for(i=n-k;i<n;i++)
y[p++]=i;/*因为第二关键字超范围,看做小于所有的第二关键字,所以他们的排名
rank[n-k]...rank[n-1]的排名为0,1,2,....所以y[rank[n-k]]...y[rank[n-1]]的值位n-k...n-1
根据sa[rank[i]]=i;化简得rank[0]..rank[k]的值为n-k...n-1
*/
for(i=0;i<n;i++)
if(sa[i]>=k) y[p++]=sa[i]-k;
/*因为这是排的第二关键字,当位于最后的后缀可能已经没有第二个已经没有第二个关键字了
这个时候就把这样的关键字都初始化为小于所有的第二关键字,而且,suffix(sa[0])<=suffix(sa[1])..
当我们排第二关键字的时候第一关键字已经没有用了,换句话说,前k个我们已经用不到了,也就是
下标小于k的,也就是sa[i]小于k的,举个例子baba这个字符串第一次算出来的sa数组为1 3 0 2,也就是
suffix(1)<=suffix(3)<=suffix(0)<suffix(2)也就是a<=a<=b<=b而第二关键字为aba@这个@是小于所有的
字符,而第一个a就没有用了,也就是sa[k]=0的就没有用了,因为sa[k]表示的是以0首字符为下标的
的排名为k这个时候我们已经不再需要排小于k的下标了,但去掉小于k的sa[]后他们的sa[]的值是相对的
如去掉0以后就剩下132了因为去掉了1个所以所有大于等于1的都得减1,同理,去掉k个就得减去k个
*/
//基数排序第一个关键字x数组存的是相当于rank[],且y[i]相当于与第一关键字,可以手动模拟一下baba
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=0;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
/*
可以换成下面的for更直观一些rank1记录的是rank的值,把y[i]看成一个下标
for(i=0;i<m;i++)
rank[i]=x[y[i]];
for(i=0;i<n;i++) c[i]=0;
for(i=0;i<m;i++)
c[rank[i]]++;
for(i=0;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--)
rank1[y[i]]==--c[x[y[i]]];
for(i=n-1;i>=0;i--)
sa[rank1[y[i]]]=y[i];
把rank1=--c[x[y[i]]]带入得sa[--c[x[y[i]]]]=y[i];
这个时候sa数组存的已经是倍增后的值但是这个时候rank[sa[i]]=i还不一定成立所以需要在下面进行验证
*/
//根据sa和y数组计算新的x数组(这时说的y是x数组和y数组交换后的y数组也就相当于rank数组)
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]+k]==y[sa[i]+k]?p-1:p++;
if(p>=n) break;
m=p;
/*
如果r[sa[i-1]]==r[sa[i]],那么,说明在以a,b为开始的l长度的字符串内肯定不包括@(这也是最后
一个元素给@的原因),所以sa[i-1]+k,sa[i]+k都小于n-k,所以此处数组不会越界。若果不相等那么
根据&&的短路特性后边的sa[i]+k就不会判断了
*/
}
}
int main()
{
return 0;
}
/*
dcba
aabb
baba
bababa
bbaa
aaab
*/
后缀数组之倍增算法