最小表示

最小表示

在这里插入图片描述

题解

很简单的一道题

由于它要求的是本质不同的字符串个数,其实我们可以把它所有本质不同的子串求出来,统计一下。
怎么求所有子串呢?后缀数组!

但我们怎么将它们转化成本质不同的形式呢?我们可以先将每个后缀都转化成本质不同的形式,再排序。
那么,答案就是 n ( n + 1 ) 2 − ∑ i = 1 n − 1 l c p ( s u f i , s u f i + 1 ) \frac{n(n+1)}{2}-\sum_{i=1}^{n-1}lcp(suf_i,suf_{i+1}) 2n(n+1)i=1n1lcp(sufi,sufi+1)
也就是总的子串数减去相邻两个的最长公共前缀长度。
这应该很容易理解,如果一个一个后缀去统计的话,后统计到的后缀,前面部分应该已经被前面统计过了,而前面所有子串与该串重合的最长长度就是 l c p ( s u f i , s u f i + 1 ) lcp(suf_i,suf_{i+1}) lcp(sufi,sufi+1),只有这部分是被统计过的,减去即可。

关键是我们怎么将一个后缀转化成本质不同的形式,再比较公共前缀呢?
这一切都可以用哈希解决。我们记录下该后缀每种颜色的对应排名与它在后面的出现状态,那么对于该后缀的哈希就只需要将每种颜色单独的加起来即可。
比较公共前缀只需要用哈希加二分求出它们的最长公共前缀长度,在比较公共前缀后面一位即可。
在我们钱所有后缀都排好序后,减去相邻两个的lcp即可。

时间复杂度 O ( n m l o g 2 n ) O\left(nmlog^2n\right) O(nmlog2n)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define MAXN 500005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
const int mo=1e9+9;
const int jzm=641;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,a[MAXN],sum[MAXN][12],id[MAXN][12],powk[MAXN],invk[MAXN],pos[12],sa[MAXN];char str[MAXN];
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
struct ming{int col,wh;}b[12];
bool cmp1(ming x,ming y){return x.wh<y.wh;}
int Hash(int l,int r){
	int res=0;
	for(int i=1;i<=m;i++)
		res=add(res,1ll*id[l][i]*add(sum[r][i],mo-sum[l-1][i])%mo);
	return 1ll*invk[l]*res%mo;
}
int lcp(int x,int y){
	int l=0,r=min(n-x,n-y);
	while(l<r){
		int mid=l+r+1>>1;
		if(Hash(x,x+mid)==Hash(y,y+mid))l=mid;
		else r=mid-1;
	}
	return l;
}
bool cmp2(int x,int y){
	int len=lcp(x,y);if(len==n-x)return 1;if(len==n-y)return 0;
	return id[x][a[x+len+1]]<id[y][a[y+len+1]];
}
void init(){
	powk[0]=invk[0]=1;const int inv=qkpow(jzm,mo-2);
	for(int i=1;i<=n;i++)powk[i]=1ll*powk[i-1]*jzm%mo,invk[i]=1ll*invk[i-1]*inv%mo;
	for(int i=1;i<=m;i++)pos[i]=n+1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j];
		sum[i][a[i]]=add(sum[i][a[i]],powk[i]);
	}
	for(int i=n;i>0;i--){
		pos[a[i]]=i;for(int j=1;j<=m;j++)b[j]=(ming){j,pos[j]};
		sort(b+1,b+m+1,cmp1);for(int j=1;j<=m;j++)id[i][b[j].col]=j;
	}
}
signed main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	read(n);read(m);scanf("%s",str+1);
	for(int i=1;i<=n;i++)a[i]=str[i]-'a'+1;
	init();for(int i=1;i<=n;i++)sa[i]=i;
	sort(sa+1,sa+n+1,cmp2);LL ans=1ll*n*(n+1)/2LL;
	for(int i=1;i<n;i++)ans-=1ll*lcp(sa[i],sa[i+1])+1LL;
	printf("%lld\n",ans);
	return 0;
}

谢谢!!!

posted @ 2021-04-05 15:19  StaroForgin  阅读(10)  评论(0)    收藏  举报  来源