1.模板
题目传送门:luogu P3809 【模板】后缀排序
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[1000010];
int rsort[1000010],sa[1000010],rank[1000010],y[1000010],wr[1000010],height[1000010];
int n;
bool cmp(int x,int y,int ln)
{
return wr[x]==wr[y]&&wr[x+ln]==wr[y+ln];
}
void get_sa(int m)
{
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[rank[i]=s[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[rank[i]]--]=i;
int ln=1,p=0;
while(p<n&&ln<=n)
{
int k=0;
for(int i=n-ln+1;i<=n;i++) y[++k]=i;
for(int i=1;i<=n;i++)
if(sa[i]>ln) y[++k]=sa[i]-ln;
for(int i=1;i<=n;i++) wr[i]=rank[y[i]];
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[wr[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[wr[i]]--]=y[i];
memcpy(wr,rank,sizeof(wr));
rank[sa[1]]=p=1;
for(int i=2;i<=n;i++)
{
p+=!cmp(sa[i],sa[i-1],ln);
rank[sa[i]]=p;
}
m=p;ln<<=1;
}
}
void get_height()
{
int k=0;
for(int i=1;i<=n;i++)
{
int j=sa[rank[i]-1];
if(k) k--;
while(s[j+k]==s[i+k]) k++;
height[rank[i]]=k;
}
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
get_sa(200);
//get_height();
for(int i=1;i<=n;i++)
printf("%d ",sa[i]);
}
2.可重叠最长重复子串
思路:
集训队的题解贴上吧(懒得打,我不会)。
代码:
这么水,懒得上。
3.不可重叠最长重复子串
题目传送门:luogu P2743 [USACO5.1]乐曲主题Musical Themes
思路:
集训队的题解贴上吧(懒得打,我不会)。
PS:以上只是模板题,这一道稍微改动的题目自行改一下即可(注意边界问题,特别烦)。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int s[10010];
int rsort[10010],sa[10010],rank[10010],y[10010],wr[10010],height[10010];
int n,ans=0;
bool cmp(int x,int y,int ln)
{
return wr[x]==wr[y]&&wr[x+ln]==wr[y+ln];
}
void get_sa(int m)
{
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[rank[i]=s[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[rank[i]]--]=i;
int ln=1,p=0;
while(p<n&&ln<=n)
{
int k=0;
for(int i=n-ln+1;i<=n;i++) y[++k]=i;
for(int i=1;i<=n;i++)
if(sa[i]>ln) y[++k]=sa[i]-ln;
for(int i=1;i<=n;i++) wr[i]=rank[y[i]];
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[wr[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[wr[i]]--]=y[i];
memcpy(wr,rank,sizeof(wr));
rank[sa[1]]=p=1;
for(int i=2;i<=n;i++)
{
p+=!cmp(sa[i],sa[i-1],ln);
rank[sa[i]]=p;
}
m=p;ln<<=1;
}
}
void get_height()
{
int k=0;
for(int i=1;i<=n;i++)
{
int j=sa[rank[i]-1];
if(k) k--;
while(s[j+k]==s[i+k]) k++;
height[rank[i]]=k;
}
}
bool check(int x)
{
int mi=sa[1],ma=sa[1];
for(int i=2;i<=n;i++)
if(height[i]<x)
{
mi=ma=sa[i];
}
else
{
ma=max(ma,sa[i]);
mi=min(mi,sa[i]);
if(ma-mi+1>x) return true;
}
return false;
}
int main()
{
int x,y=0;
scanf("%d",&n);
scanf("%d",&y);
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
s[i-1]=x-y+100;
y=x;
}
n--;
get_sa(200);
get_height();
int l=1,r=(n>>1),mid;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid; else r=mid-1;
}
printf("%d",ans>=4?ans+1:0);
}
4.可重叠k次最长重复子串
题目传送门:luogu P2852 [USACO06DEC]牛奶模式Milk Patterns
思路:
集训队的题解贴上吧(懒得打,我不会)。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{int x,id;} d[20010];
int s[20010];
int rsort[20010],sa[20010],rank[20010],y[20010],wr[20010],height[20010];
int n,k,ans=0;
bool cmp1(node x,node y)
{
return x.x<y.x;
}
bool cmp2(int x,int y,int ln)
{
return wr[x]==wr[y]&&wr[x+ln]==wr[y+ln];
}
void get_sa(int m)
{
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[rank[i]=s[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[rank[i]]--]=i;
int ln=1,p=0;
while(p<n&&ln<=n)
{
int k=0;
for(int i=n-ln+1;i<=n;i++) y[++k]=i;
for(int i=1;i<=n;i++)
if(sa[i]>ln) y[++k]=sa[i]-ln;
for(int i=1;i<=n;i++) wr[i]=rank[y[i]];
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[wr[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[wr[i]]--]=y[i];
memcpy(wr,rank,sizeof(wr));
rank[sa[1]]=p=1;
for(int i=2;i<=n;i++)
{
p+=!cmp2(sa[i],sa[i-1],ln);
rank[sa[i]]=p;
}
m=p;ln<<=1;
}
}
void get_height()
{
int k=0;
for(int i=1;i<=n;i++)
{
int j=sa[rank[i]-1];
if(k) k--;
while(s[j+k]==s[i+k]) k++;
height[rank[i]]=k;
}
}
bool check(int x)
{
int t=0;
for(int i=1;i<=n;i++)
if(height[i]>=x)
{
t++;
if(t>=k-1) return true;
}
else
{
t=0;
}
return false;
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&d[i].x);
d[i].id=i;
}
sort(d+1,d+n+1,cmp1);
int last=-1,pos=0;
for(int i=1;i<=n;i++)
if(d[i].x==last) s[d[i].id]=pos; else last=d[i].x,s[d[i].id]=++pos;
get_sa(20000);
get_height();
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid; else r=mid-1;
}
printf("%d",ans);
}
5.不相同的字串个数
题目传送门:luogu SP694 DISUBSTR - Distinct Substrings
思路:
集训队的题解贴上吧(懒得打,我不会)。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[1010];
int rsort[1010],sa[1010],rank[1010],y[1010],wr[1010],height[1010];
int n;
bool cmp(int x,int y,int ln)
{
return wr[x]==wr[y]&&wr[x+ln]==wr[y+ln];
}
void get_sa(int m)
{
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[rank[i]=s[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[rank[i]]--]=i;
int ln=1,p=0;
while(p<n&&ln<=n)
{
int k=0;
for(int i=n-ln+1;i<=n;i++) y[++k]=i;
for(int i=1;i<=n;i++)
if(sa[i]>ln) y[++k]=sa[i]-ln;
for(int i=1;i<=n;i++) wr[i]=rank[y[i]];
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[wr[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[wr[i]]--]=y[i];
memcpy(wr,rank,sizeof(wr));
rank[sa[1]]=p=1;
for(int i=2;i<=n;i++)
{
p+=!cmp(sa[i],sa[i-1],ln);
rank[sa[i]]=p;
}
m=p;ln<<=1;
}
}
void get_height()
{
int k=0;
for(int i=1;i<=n;i++)
{
int j=sa[rank[i]-1];
if(k) k--;
while(s[j+k]==s[i+k]) k++;
height[rank[i]]=k;
}
}
int solve()
{
int ans=0;
for(int i=1;i<=n;i++)
ans+=n-sa[i]+1-height[i];
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
n=strlen(s+1);
get_sa(200);
get_height();
printf("%d\n",solve());
}
}
6.最长回文子串
思路:
manacher(O(n))即可。
当然后缀数组+RMQ也可以。
集训队的题解贴上吧(懒得打,我不会)。
代码:
懒得打。
7.连续重复子串
题目传送门:luogu UVA10298 Power Strings
思路:
KMP即可。我们发现len-next[len]的值即为重复字串的长度,再判断一下即可。
当然后缀数组也可以。
集训队的代码贴上吧(懒得打,我不会)。
代码(KMP的):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char a[2000000];
int la;
int p[2000000];
int main()
{
while(1)
{
scanf("%s",a+1);
if(a[1]=='.') return 0;
la=strlen(a+1);
p[1]=0;
int j=0;
for(int i=2;i<=la;i++)
{
while(j>0&&a[i]!=a[j+1]) j=p[j];
if(a[i]==a[j+1]) j++;
p[i]=j;
}
printf("%d\n",la%(la-p[la]+1-1)?1:la/(la-p[la]+1-1));
}
}
7.重复次数最多的连续重复子串
题目传送门:poj 2774 Long Long Message
思路:
坑。
代码:
坑。
8.最长公共子串
题目传送门:poj 3693 Maximum repetition substring
思路:
集训队的题解有些啰嗦,我简化一下(别D我)。
将两个字符串拼在一起,中间用一个乱七八糟的字符隔开。设s1的长度为l1,s2的长度为s2,拼成s。显然的,两个字符串的公共子串必然是排完序后相邻的两个字符串,且sa值在1~l1和l1+1~l1+1+l2的两个字符串(因为最长公共子串是s1和s2的,必须在这两个之中)。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[200010],s1[200010],s2[200010];
int rsort[200010],sa[200010],rank[200010],y[200010],wr[200010],height[200010];
int n,l1,l2;
bool cmp(int x,int y,int ln)
{
return wr[x]==wr[y]&&wr[x+ln]==wr[y+ln];
}
void get_sa(int m)
{
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[rank[i]=s[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[rank[i]]--]=i;
int ln=1,p=0;
while(p<n&&ln<=n)
{
int k=0;
for(int i=n-ln+1;i<=n;i++) y[++k]=i;
for(int i=1;i<=n;i++)
if(sa[i]>ln) y[++k]=sa[i]-ln;
for(int i=1;i<=n;i++) wr[i]=rank[y[i]];
for(int i=0;i<=m;i++) rsort[i]=0;
for(int i=1;i<=n;i++) rsort[wr[i]]++;
for(int i=2;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[wr[i]]--]=y[i];
memcpy(wr,rank,sizeof(wr));
rank[sa[1]]=p=1;
for(int i=2;i<=n;i++)
{
p+=!cmp(sa[i],sa[i-1],ln);
rank[sa[i]]=p;
}
m=p;ln<<=1;
}
}
void get_height()
{
int k=0;
for(int i=1;i<=n;i++)
{
int j=sa[rank[i]-1];
if(k) k--;
while(s[j+k]==s[i+k]) k++;
height[rank[i]]=k;
}
}
int solve()
{
int ans=0;
for(int i=2;i<=n;i++)
if((sa[i-1]<=l1&&sa[i]>l1+1)||(sa[i-1]>l1+1&&sa[i]<=l1)) ans=max(ans,height[i]);
return ans;
}
int main()
{
scanf("%s",s1+1);
l1=strlen(s1+1);
for(int i=1;i<=l1;i++)
s[i]=s1[i];
s[l1+1]='$';
scanf("%s",s2+1);
l2=strlen(s2+1);
for(int i=1;i<=l2;i++)
s[l1+1+i]=s2[i];
n=l1+1+l2;
get_sa(200);
get_height();
printf("%d",solve());
}