Description
一个字符串的前缀是指包含该字符第一个字母的连续子串,例如:abcd的所有前缀为a, ab, abc, abcd。
给出一个字符串S,求其所有前缀中,字符长度与出现次数的乘积的最大值。
例如:S = “abababa” 所有的前缀如下:
“a”, 长度与出现次数的乘积 1 * 4 = 4,
“ab”,长度与出现次数的乘积 2 * 3 = 6,
“aba”, 长度与出现次数的乘积 3 * 3 = 9,
“abab”, 长度与出现次数的乘积 4 * 2 = 8,
“ababa”, 长度与出现次数的乘积 5 * 2 = 10,
“ababab”, 长度与出现次数的乘积 6 * 1 = 6,
“abababa”, 长度与出现次数的乘积 7 * 1 = 7.
其中”ababa”出现了2次,二者的乘积为10,是所有前缀中最大的。
题解
考虑next数组求的是什么东西?它求的是每一个以 i 为结尾的前缀的最长的前缀,使得这个前缀是它的后缀。那么,只要一个前缀出现了一次,它一定就是某一个前缀的后缀,那么这个后缀不停的取next就一定能够取到这个前缀,所以只要构造一棵next的树,每一个前缀出现的次数就是以它为根的子树大小(包括他自己)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define LL long long
using namespace std;
inline char nc(){
static char buf[100000],*i=buf,*j=buf;
return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int read_s(char *x){
char ch=nc();int len=0;
while(ch!=EOF&&ch!='\n')*++x=ch-48,ch=nc(),len++;
return len;
}
int n,tot,p[maxn],num[maxn],lnk[maxn],son[maxn],nxt[maxn];
LL ans;
char a[maxn];
void add(int x,int y){
nxt[++tot]=lnk[x];son[tot]=y;lnk[x]=tot;
}
void dfs(int x){
num[x]=1;
for(int j=lnk[x];j;j=nxt[j]){
dfs(son[j]);
num[x]+=num[son[j]];
}
ans=max(ans,(LL)x*num[x]);
}
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
n=read_s(a);add(0,1);
for(int i=2;i<=n;i++){
int j=p[i-1];
while(j&&a[j+1]!=a[i])j=p[j];
if(a[j+1]==a[i])j++;
p[i]=j;add(p[i],i);
}
dfs(0);
printf("%lld\n",ans);
return 0;
}