AcWing 2766. 后缀自动机

后缀数组解法

前置题目

Solution

一个字符串的后缀的所有前缀恰好不重不漏地覆盖了所有的子串,所以我们可以考虑用后缀数组来做。

比如:babab
排完序后

  1. ab
  2. abab
  3. b
  4. bab
  5. babab

(以下后缀均指排名为 \(i\) 的后缀,而不是原串的第 \(i\) 个后缀)
一个后缀 \(i\) 的一个长度为 \(len\) 的前缀的出现次数是 使 \(lcp(i,j) \geq len\) 成立的 \(j\) 最大值与最小值之差+1.
在后缀数组中,我们有 \(lcp(i,j)=\min(lcp(i,i+1),...,lcp(j-1,j)),i<j\)
因此 \(lcp(i,j) \leq lcp(i,i+1),...,lcp(j-1,j)\)
因此只需要在 height[] 求出左边和右边 height[] 小于 height[i] 的第一个 j,即为使 \(lcp(i,j) \geq len\) 的成立的 \(j\) 最小值-1与最大值+1,同时之后的 \(j\) 一定不能使 \(lcp(i,j) \geq len\) 成立。
还要乘一个子串长度:把子串长度看作高,把 \(|jmax-jmin|\) 看作宽。
由此可以抽象为 131. 直方图中最大的矩形 来写。

Code

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

const int N=1e6+5;

int n,m;
char s[N];
int sa[N],rk[N],ht[N];
// sa[i] 表示排名为i的后缀是 sa[i] 
void get_sa()
{
	static int x[N],y[N],c[N];
	int i,k,num;
	memset(c,0,sizeof c);
	for(i=1;i<=n;i++) c[x[i]=s[i]]++;
	for(i=1;i<=m;i++) c[i]+=c[i-1];
	for(i=n;i>=1;i--) sa[c[x[i]]--]=i;
	
	for(k=1;k<=n;k<<=1) {
		num=0;
		for(i=n-k+1;i<=n;i++) y[++num]=i;
		for(i=1;i<=n;i++) 
			if(sa[i]>k) y[++num]=sa[i]-k;
		
		for(i=1;i<=m;i++) c[i]=0;
		for(i=1;i<=n;i++) c[x[i]]++;
		for(i=1;i<=m;i++) c[i]+=c[i-1];
		for(i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
		
		swap(x,y);
		x[sa[1]]=1,num=1;
		for(i=2;i<=n;i++) 
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
		if(num==n) break;
		m=num;
	} 
	for(i=1;i<=n;i++) rk[sa[i]]=i;
//	for(i=1;i<=n;i++) printf("%d ",sa[i]);
//	printf("\n");
}

void get_ht()
{
	int i,j,k=0;
	// dim h[i]=height[rk[i]]
	// h[i] >= h[i-1]-1;
	for(i=1;i<=n;i++) {
		if(rk[i]==1) continue;
		if(k) k--;
		j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
		ht[rk[i]]=k; 
	}
//	for(i=1;i<=n;i++) printf("%d ",ht[i]);
//	printf("\n");	
}

int pl[N],pr[N]; // ht[] 左边和右边第一个更小的数。 

void calc(int a[],int p[])
{
	static int stk[N],top;
	memset(stk,0,sizeof stk);
	top=0,stk[top]=1;

	for(int i=2;i<=n;i++) {
		while(top>=0&&a[stk[top]]>=a[i]) top--;
		p[i]=stk[top];
		stk[++top]=i;
	}
}
LL ans;

int main()
{
//	freopen("1.in","r",stdin);
	int i;
	scanf("%s",s+1);
	n=strlen(s+1),m=122;
	get_sa(),get_ht();
	
	calc(ht,pl);
	reverse(ht+2,ht+n+1);
	calc(ht,pr);
	reverse(ht+2,ht+n+1);
	reverse(pr+2,pr+n+1);
	for(i=2;i<=n;i++) pr[i]=n+2-pr[i];
//	for(i=1;i<=n;i++) printf("%d ",pr[i]);
//	printf("\n");	
	for(i=2;i<=n;i++) {
		if(pr[i]-pl[i]<=1) continue;
		ans=max(ans,(LL)(pr[i]-pl[i])*(LL)ht[i]);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-11-28 21:03  cjlworld  阅读(62)  评论(0编辑  收藏  举报