Kyoto University Programming Contest 2016 G题Exam(后缀数组)

传送门

题意

有一个字符串集合S和字符串T,满足以下条件:

  1. S中任意字符串都是T的一个连续子串
  2. 在S中每一对不同的字符串x,y,x不为y的子串。
    给出T,求S集合中可能的最大字符串数目。

数据范围

\[1\leq |T|\leq 10^5 \]

|T|表示字符串T的长度。

题解

假设当前\(S\)集合中有\(x\)个字符串,每一个对应T的\([l_i,r_i]\),因为题目要求每一个字符串不为另一字符串的子串,可以推断出:将S中所有字符串按照\(l_i\)排序一定有\(l_1<l_2<...<l_x\)\(r_1<r_2<...<r_x\)。假设当前的所有后缀最长公共前缀为LCP,对于每一个字符串取区间\([i,i+LCP](1\leq i\leq n-LCP)\),此时,集合中每一个字符串长度相等,两个长度相等的字符串要不为子串关系,即为不相等。因为当前每一个字符串都是后缀字符串的一个前缀,且长度为LCP+1,故这么取出来的所有字符串一定不会相等,数目有\(n-LCP\)个。

我们假设从x起始的后缀字符串与从y起始的后缀字符串最长公共前缀为LCP,则若x,y都作为左端点加入集合时,其长度一定要大于LCP,对于一个区间长度为LCP+1的字符串\([i,i+LCP]\),要保证后面所有字符串不相互包含,要么大于x的位置作为左端点时区间长度都要大于LCP,要么[i+1,i+LCP]都不作为左端点。前者一定会牺牲最后LCP个位置作为左端点,后者也少了LCP个位置当左端点,所以总数目不会超过\(n-LCP\)。若x,y不全加入集合,则能作为左端点的位置-1,此时所有后缀的最长公共前缀为LCP-1,此时又是一个新的状态,以此递推,最终数目也不会超过\(n-LCP\)

代码

/*************************************************************************
	> File Name: 2.cpp
	> Author: Knowledge_llz
	> Mail: 925538513@qq.com 
	> Blog: https: https://www.cnblogs.com/Knowledge-Pig/ 
	> Created Time: 2021/1/29 15:05:35
 ************************************************************************/

#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define LL long long
#define pb push_back
#define fi first
#define se second
#define pr pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
int read(){
	char x=getchar(); int u=0,fg=0;
	while(!isdigit(x)){ if(x=='-') fg=1; x=getchar(); }
	while(isdigit(x)){ u=(u<<3)+(u<<1)+(x^48); x=getchar(); }
	return fg?-u:u;
}
const int maxx=1e5+10;
char s[maxx];
int n,m,x[maxx],y[maxx],sa[maxx],rk[maxx],height[maxx],c[maxx],x_new[maxx];
void get_sa(){
	For(i,1,n) ++c[x[i]=s[i]];
	For(i,2,m) c[i]+=c[i-1];
	for(int i=n;i>=1;--i) sa[c[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1){
		int num=0;
		For(j,n-k+1,n) y[++num]=j;
		For(j,1,n)	if(sa[j]>k) y[++num]=sa[j]-k;
		For(j,1,m) c[j]=0;
		For(j,1,n) ++c[x[j]];
		For(j,2,m) c[j]+=c[j-1];
		for(int j=n;j>=1;--j) sa[c[x[y[j]]]--]=y[j];
		x_new[sa[1]]=1; num=1;
		for(int j=2;j<=n;++j)
			x_new[sa[j]]=(x[sa[j]]==x[sa[j-1]] && x[sa[j]+k]==x[sa[j-1]+k]) ? num : ++num;
		for(int j=1;j<=n;++j) x[j]=x_new[j];
		if(num==n) break;
		m=num;
	}
}

void get_height(){
	int k=0;
	For(i,1,n) rk[sa[i]]=i;
	For(i,1,n){
		if(rk[i]==1) continue;
		if(k>0) --k;
		int j=sa[rk[i]-1];
		while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) ++k;
		height[rk[i]]=k;
	}
}


int main()
{
#ifndef ONLINE_JUDGE
	freopen("input.in", "r", stdin);
	freopen("output.out", "w", stdout);
#endif
	scanf("%s",s+1);
	n=strlen(s+1); m=122;
	get_sa();
	get_height();
	int ans=0;
	For(i,2,n) ans=max(ans,height[i]);
	printf("%d\n",n-ans);
	return 0;
}
posted @ 2021-01-30 22:46  Knowledge-Pig  阅读(85)  评论(0)    收藏  举报