简单介绍:
在字符串处理当中,后缀树和后缀数组都是非常有力的工具。其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。可以说,在信息学竞赛中后缀数组比后缀树要更为实用。
作用:
后缀数组能快速比较两个后缀的字典序大小,它还能求多个字符串两两的LCP。
实现:
后缀数组一般是用倍增做的,总的时间复杂度是:,空间复杂度是:
排序是用基数排序来实现的。
代码:
#include
using namespace std;
const int N=1e6+35;
int n,p,c[N],a[N],sa[N],x[N],y[N];
//sa[i]表示排名为i的后缀的起点
//c[]用于基数排序
char s[N];
void SA(int n,int m)
{
for(int i=1;i<=m;i++)c[i]=0;//清空鸡排数组
for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];//前缀和
for(int i=n;i>0;i--)sa[c[x[i]]--]=i;//第一轮的sa数组
for(int i=1;i;i=i*2)//倍增
{
p=0;
for(int j=n-i+1;j<=n;j++)y[++p]=j;//倍增个数不够,要补0
for(int j=1;j<=n;j++)
if(sa[j]>i)y[++p]=sa[j]-i;
for(int j=1;j<=m;j++)c[j]=0;//清空鸡排数组
for(int j=1;j<=n;j++)c[x[y[j]]]++;
for(int j=1;j<=m;j++)c[j]+=c[j-1];
for(int j=n;j>0;j--)sa[c[x[y[j]]]--]=y[j];
swap(x,y);//注意
x[sa[1]]=1;
p=2;//注意
for(int j=2;j<=n;j++)
{
if(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])x[sa[j]]=p-1;//相同
else x[sa[j]]=p++;//不相同
}
if(p>n)return;//全部不相同
m=p;//小优化
}
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)a[i]=s[i];
SA(n,233);
for(int i=1;i<=n;i++)printf("%d ",sa[i]);
return 0;
} 题目:1031: [JSOI2007]字符加密Cipher
作用2:
后缀数组求多个字符串两两的LCP。
这是怎么做的呢?思想是把多个字符串拼成一个串,两个串之间用“#”隔开。
height数组定义:定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀。
对于j和k,不妨设rank[j]<rank[k],则有以下性质:
suffix(j)和suffix(k)的最长公共前缀为:
height[rank[j]+1], height[rank[j]+2], height[rank[j]+3], …, height[rank[k]]中的最小值。
所以这个可以用倍增即st表来完成。
题目:[P1117 [NOI2016]优秀的拆分
代码:
#include
using namespace std;
const int N=3e5+5;
char a[N];
int T,n,p,Log[N],f[N],g[N];
struct node{
int sa[N],rk[N],height[N],c[N],x[N],y[N],st[N][25];
void SA(int n,int m)
{
memset(sa,0,sizeof(sa));
memset(rk,0,sizeof(rk));
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i;i--)sa[c[x[i]]--]=i;
for(int i=1;i;i=i*2)
{
p=0;
for(int j=n-i+1;j<=n;j++)y[++p]=j;
for(int j=1;j<=n;j++)
if(sa[j]>i)y[++p]=sa[j]-i;
for(int j=1;j<=m;j++)c[j]=0;
for(int j=1;j<=n;j++)c[x[y[j]]]++;
for(int j=1;j<=m;j++)c[j]+=c[j-1];
for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
swap(x,y);
x[sa[1]]=1;
p=2;
for(int j=2;j<=n;j++)
{
if(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])x[sa[j]]=p-1;
else x[sa[j]]=p++;
}
if(p>n)return;
m=p;
}
}
void Height()//求出height数组,height[i]=lcp(sa[i-1],sa[i])
{
memset(height,0,sizeof(height));
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;//rk[i]表示起点在i的后缀的排名
for(int i=1;i<=n;i++)
{
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k])k++;
height[rk[i]]=k;
}
}
void ST()//倍增求min{height[]}
{
memset(st,0x3f,sizeof(st));
for(int i=1;i<=n;i++)st[i][0]=height[i];
for(int i=1;i<=15;i++)
for(int j=1;j<=n;j++)
st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
}
int query(int l,int r)
{
int L=l,R=r;
l=min(rk[L],rk[R])+1;
r=max(rk[L],rk[R]);
int t=Log[r-l+1];
return min(st[l][t],st[r-(1<<t)+1][t]);
}
}A,B;
void work()
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
scanf("%s",a+1);
n=strlen(a+1);
A.SA(n,122);
A.Height();
A.ST();
reverse(&a[1],&a[n+1]);
B.SA(n,122);
B.Height();
B.ST();
for(int len=1;len<=n/2;len++)
{
for(int i=len,j=i+len;j<=n;i+=len,j+=len)
{
int lcp=min(A.query(i,j),len),lcs=min(B.query(n-i+2,n-j+2),len-1);
int t=lcp+lcs-len+1;
if(lcp+lcs>=len)
{
g[i-lcs]++;g[i-lcs+t]--;
f[j+lcp-t]++;f[j+lcp]--;
}
}
}
for(int i=1;i<=n;i++)f[i]+=f[i-1],g[i]+=g[i-1];
long long ans=0;
for(int i=1;i<n;i++)ans+=1ll*f[i]*g[i+1];
printf("%lld\n",ans);
}
int main()
{
Log[0]=-1;
for(int i=1;i>1]+1;
scanf("%d",&T);
while(T--)work();
return 0;
} 题目:P4341 [BJWC2010]外星联络
代码:
#include
using namespace std;
const int N=1e6+5;
int n,a[N],x[N],y[N],c[N],sa[N],rk[N],height[N];
char s[N];
/*char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}*/
#define gc getchar
inline int read()
{
int ret=0,f=0;char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){ret=ret*10+c-48;c=gc();}
if(f)return -ret;return ret;
}
void SA(int n,int m)
{
int p;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i;i--)sa[c[x[i]]--]=i;
for(int i=1;i;i=i*2)
{
p=0;
for(int j=n-i+1;j<=n;j++)y[++p]=j;
for(int j=1;j<=n;j++)
if(sa[j]>i)y[++p]=sa[j]-i;
for(int j=1;j<=m;j++)c[j]=0;
for(int j=1;j<=n;j++)c[x[y[j]]]++;
for(int j=1;j<=m;j++)c[j]+=c[j-1];
for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
swap(x,y);
p=2;
x[sa[1]]=1;
for(int j=2;j<=n;j++)
if(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])x[sa[j]]=p-1;
else x[sa[j]]=p++;
if(p>n)return;
m=p;
}
}
void Height()
{
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1;i<=n;i++)
{
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k])k++;
height[rk[i]]=k;
}
}
int main()
{
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++)a[i]=s[i];
SA(n,233);
Height();
for(int i=2;i<=n;i++)
{
for(int j=height[i-1]+1;j<=height[i];j++)
{
int k=i;
while(height[k]>=j)k++;
printf("%d\n",k-i+1);
}
}
return 0;
} 
京公网安备 11010502036488号