后缀数组整理

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());
}

猜你喜欢

转载自blog.csdn.net/zsyz_ZZY/article/details/81774063