后缀数组解法
前置题目
Solution
一个字符串的后缀的所有前缀恰好不重不漏地覆盖了所有的子串,所以我们可以考虑用后缀数组来做。
比如:babab
排完序后
ab
abab
b
bab
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("
");
}
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("
");
}
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("
");
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
",ans);
return 0;
}