我感觉后缀数组算法不好理解,我是看的 2009年国家集训队论文 《后缀数组---处理字符串的有力工具》来学的后缀数组算法。
这里只是附上倍增算法的模板,不对算法进行讲解。
int wa[maxn],wb[maxn],wv[maxn],w[maxn];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) w[i]=0;
for(i=0;i<n;i++) w[x[i]=r[i]]++;
for(i=1;i<m;i++) w[i]+=w[i-1];
for(i=n-1;i>=0;i--) sa[--w[x[i]]]=i;
for(j=1,p=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]];
for(i=0;i<m;i++) w[i]=0;
for(i=0;i<n;i++) w[wv[i]]++;
for(i=1;i<m;i++) w[i]+=w[i-1];
for(i=n-1;i>=0;i--) sa[--w[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
下面是后缀数组的一个应用题目
题目链接:https://vjudge.net/problem/SPOJ-DISUBSTR
Distinct Substrings
Given a string, we need to find the total number of its distinct substrings.
Input
T- number of test cases. T<=20;
Each test case consists of one string, whose length is <= 1000Output
For each test case output one number saying the number of distinct substrings.
Example
Sample Input:
2
CCCCC
ABABASample Output:
5
9Explanation for the testcase with string ABABA:
len=1 : A,B
len=2 : AB,BA
len=3 : ABA,BAB
len=4 : ABAB,BABA
len=5 : ABABA
Thus, total number of distinct substrings is 9.
题目大意:给你一个字符串,求这个字符串有多少个不同的子字符串。
思路:直接根据09年oi论文<<后缀数组——出来字符串的有力工具>>的解法。
还有另一种思想:总数为n*(n-1)/2,height[i]是两个后缀的最长公共前缀,所以用总数-height[i]的和就是答案
注:拥有相同长的lcp的两个后缀一定挨在一起
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;
int T;
char s[maxn];
int f[maxn],suf[maxn];
int rank[maxn],height[maxn];
//模板
int wa[maxn],wb[maxn],wv[maxn],w[maxn];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) w[i]=0;
for(i=0;i<n;i++) w[x[i]=r[i]]++;
for(i=1;i<m;i++) w[i]+=w[i-1];
for(i=n-1;i>=0;i--) sa[--w[x[i]]]=i;
for(j=1,p=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]];
for(i=0;i<m;i++) w[i]=0;
for(i=0;i<n;i++) w[wv[i]]++;
for(i=1;i<m;i++) w[i]+=w[i-1];
for(i=n-1;i>=0;i--) sa[--w[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
m=p;
}
return;
}
int main()
{
cin>>T;
while(T--)
{
scanf("%s",s);
int cnt=strlen(s);
int i,j,k;
for(i=0;i<cnt;i++)
{
f[i]=s[i];
}
f[cnt]=0;
da(f,suf,cnt+1,256);
for(i=1;i<=cnt;i++)
{
rank[suf[i]]=i;
}
for(k=0,i=0;i<cnt;i++){
if(k)k--;
j=suf[rank[i]-1];
while(f[i+k]==f[j+k])k++;
height[i]=k;//在这个题目里,这样写也可
}
int sum=0;
for(i=0;i<cnt;i++)
{
sum=sum+cnt-i-height[i];
}
printf("%d\n",sum);
}
return 0;
}