一、模板:
①倍增nlogn:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=1000005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int N,M;
void Qsort(void)
{
for(int i=0;i<=M;i++) tax[i]=0;
for(int i=1;i<=N;i++) tax[rak[i]]++;
for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}
bool cmp(int *f,int x,int y,int w)
{
return f[x]==f[y]&&f[x+w]==f[y+w];
}
void suffixsort(void)
{
M=128;
for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
Qsort();
for(int w=1,p=0;p<N;M=p,w<<=1)
{
p=0;
for(int i=1;i<=w;i++) tp[++p]=N-w+i;
for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
for(int i=2;i<=N;i++)
rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
}
}
void getheight(void)
{
int k=0;
for(int i=1;i<=N;i++)
{
if(k) k--;
int j=sa[rak[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rak[i]]=k;
}
}
int main(void)
{
scanf("%s",str+1);
N=strlen(str+1);
suffixsort();
getheight();
for(int i=1;i<=N;i++)
{
if(i!=1) putchar(' ');
printf("%d",sa[i]);
}
putchar('\n');
return 0;
}
②DC3算法:n
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
using namespace std;
const int maxn=1000005;
int wa[maxn*3],wb[maxn*3],wv[maxn*3],wss[maxn*3];
int r[maxn*3],sa[maxn*3],height[maxn],rak[maxn];
char str[maxn*3];
int N,M;
int c0(int *r,int a,int b)
{
return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b)
{
if(k==2)
return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
else return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
}
void Qsort(int *r,int *a,int *b,int n,int m)
{
for(int i=0;i<n;i++) wv[i]=r[a[i]];
for(int i=0;i<m;i++) wss[i]=0;
for(int i=0;i<n;i++) wss[wv[i]]++;
for(int i=1;i<m;i++) wss[i]+=wss[i-1];
for(int i=n-1;i>=0;i--)
b[--wss[wv[i]]]=a[i];
}
void dc3(int *r,int *sa,int n,int m)
{
int i,j,*rn=r+n;
int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
r[n]=r[n+1]=0;
for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
Qsort(r+2,wa,wb,tbc,m);
Qsort(r+1,wb,wa,tbc,m);
Qsort(r,wa,wb,tbc,m);
rn[F(wb[0])]=0,p=1;
for(i=1;i<tbc;i++)
rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
if(p<tbc) dc3(rn,san,tbc,p);
else for(i=0;i<tbc;i++) san[rn[i]]=i;
for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
if(n%3==1) wb[ta++]=n-1;
Qsort(r,wb,wa,ta,m);
for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
for(i=0,j=0,p=0;i<ta&&j<tbc;p++)
sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
for(;i<ta;p++) sa[p]=wa[i++];
for(;j<tbc;p++) sa[p]=wb[j++];
}
void da(int str[],int sa[],int rak[],int height[],int n,int m)
{
for(int i=n;i<n*3;i++)
str[i]=0;
dc3(str,sa,n+1,m);
int k=0;
for(int i=0;i<=n;i++) rak[sa[i]]=i;
for(int i=0;i<n;i++)
{
if(k) k--;
int j=sa[rak[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rak[i]]=k;
}
}
int main(void)
{
scanf("%s",str);
N=strlen(str);
for(int i=0;i<N;i++)
r[i]=str[i];
M=128;
da(r,sa,rak,height,N,M);
for(int i=1;i<=N;i++)
{
if(i!=1) putchar(' ');
printf("%d",sa[i]+1);
}
putchar('\n');
return 0;
}
二、例题:
①本质不同的子串的个数:
每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。
每个后缀suffix(sa(i))的贡献是len-sa(i)+1,但是有子串重复,重复的就是height(i)个与前面相同的前缀,那么一个后缀suffix(sa(i))的贡献就是len-sa(i)+1-height(i)。
也可以由公式求出所有子串,减去重复的子串sum(height(i))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=100005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int N,M;
void Qsort(void)
{
for(int i=0;i<=M;i++) tax[i]=0;
for(int i=1;i<=N;i++) tax[rak[i]]++;
for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}
bool cmp(int *f,int x,int y,int w)
{
return f[x]==f[y]&&f[x+w]==f[y+w];
}
void suffixsort(void)
{
M=128;
for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
Qsort();
for(int w=1,p=0;p<N;M=p,w<<=1)
{
p=0;
for(int i=1;i<=w;i++) tp[++p]=N-w+i;
for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
for(int i=2;i<=N;i++)
rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
}
}
void getheight(void)
{
int k=0;
for(int i=1;i<=N;i++)
{
if(k) k--;
int j=sa[rak[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rak[i]]=k;
}
}
int main(void)
{
int pp;
scanf("%d",&pp);
scanf("%s",str+1);
N=strlen(str+1);
suffixsort();
getheight();
ll n=((ll)N*(N+1))/2;
ll m=0;
for(int i=1;i<=N;i++)
m+=height[i];
printf("%lld\n",n-m);
return 0;
}
②两个字符串的最长公共子串:
将两个字符串中间加一个未出现的字符连接起来,然后做一次后缀数组,得到height数组,如果height数组所代表的两串分别在分界符的左右两侧,就更新最大值。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=300005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int N,M;
void Qsort(void)
{
for(int i=0;i<=M;i++) tax[i]=0;
for(int i=1;i<=N;i++) tax[rak[i]]++;
for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}
bool cmp(int *f,int x,int y,int w)
{
return f[x]==f[y]&&f[x+w]==f[y+w];
}
void suffixsort(void)
{
M=128;
for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
Qsort();
for(int w=1,p=0;p<N;M=p,w<<=1)
{
p=0;
for(int i=1;i<=w;i++) tp[++p]=N-w+i;
for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
for(int i=2;i<=N;i++)
rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
}
}
void getheight(void)
{
int k=0;
for(int i=1;i<=N;i++)
{
if(k) k--;
int j=sa[rak[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rak[i]]=k;
}
}
int main(void)
{
while(scanf("%s",str+1)!=EOF)
{
N=strlen(str+1);
M=128;
str[N+1]=1;
int n=N;
scanf("%s",str+N+2);
N=strlen(str+1);
suffixsort();
getheight();
int maxx=0;
for(int i=1;i<=N;i++)
{
if(sa[i]>=n+2&&sa[i-1]<=n||sa[i]<=n&&sa[i-1]>=n+2)
maxx=max(maxx,height[i]);
}
printf("%d\n",maxx);
}
return 0;
}
③不可重叠最长重复子串:Musical Theme POJ - 1743:二分
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=300005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
int str[maxn];
int N,M;
void Qsort(void)
{
for(int i=0;i<=M;i++) tax[i]=0;
for(int i=1;i<=N;i++) tax[rak[i]]++;
for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}
bool cmp(int *f,int x,int y,int w)
{
return f[x]==f[y]&&f[x+w]==f[y+w];
}
void suffixsort(void)
{
for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
Qsort();
for(int w=1,p=0;p<N;M=p,w<<=1)
{
p=0;
for(int i=1;i<=w;i++) tp[++p]=N-w+i;
for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
for(int i=2;i<=N;i++)
rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
}
}
void getheight(void)
{
int k=0;
for(int i=1;i<=N;i++)
{
if(k) k--;
int j=sa[rak[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rak[i]]=k;
}
}
bool check(int mid)
{
for(int i=2;i<=N;i++)
{
while(i<=N&&height[i]<mid) i++;
if(i>N) break;
int minn=N;
int maxx=0;
minn=min(minn,sa[i-1]);
maxx=max(maxx,sa[i-1]);
while(i<=N&&height[i]>=mid)
{
minn=min(minn,sa[i]);
maxx=max(maxx,sa[i]);
i++;
}
if(maxx-minn>=mid) return true;
}
return false;
}
int main(void)
{
int n;
while(scanf("%d",&n),n)
{
str[0]=0;
for(int i=1;i<=n;i++)
scanf("%d",&str[i]);
for(int i=1;i<n;i++)
str[i]=str[i+1]-str[i]+89;
str[n]=0;
N=--n;
M=180;
suffixsort();
getheight();
int l=0,r=N;
int ans=0,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans<4)
printf("0\n");
else printf("%d\n",ans+1);
}
return 0;
}
④出现k次的可重叠的最长子串长度:Milk Patterns POJ - 3261:二分
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int maxn=1000005;
int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
int str[maxn];
int N,M;
int n,k;
void Qsort(void)
{
for(int i=0;i<=M;i++) tax[i]=0;
for(int i=1;i<=N;i++) tax[rak[i]]++;
for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}
bool cmp(int *f,int x,int y,int w)
{
return f[x]==f[y]&&f[x+w]==f[y+w];
}
void suffixsort(void)
{
for(int i=1;i<=N;i++) rak[i]=str[i],tp[i]=i;
Qsort();
for(int w=1,p=0;p<N;M=p,w<<=1)
{
p=0;
for(int i=1;i<=w;i++) tp[++p]=N-w+i;
for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
for(int i=2;i<=N;i++)
rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
}
}
void getheight(void)
{
int k=0;
for(int i=1;i<=N;i++)
{
if(k) k--;
int j=sa[rak[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rak[i]]=k;
}
}
bool check(int mid)
{
for(int i=2;i<=N;i++)
{
int cnt=1;
while(i<=N&&height[i]<mid)
i++;
if(i>N) break;
while(i<=N&&height[i]>=mid)
{
cnt++;
i++;
}
if(cnt>=k) return true;
}
return false;
}
int main(void)
{
while(scanf("%d%d",&n,&k)!=EOF)
{
int maxx=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&str[i]);
maxx=max(maxx,str[i]);
}
str[n+1]=-1;//防止求height数组越界
N=n;
M=maxx;
suffixsort();
getheight();
int l=0,r=N;
int ans=0,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}