51nod 1277 字符串中的最大值【KMP算法】【next树】

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 <script type="math/tex" id="MathJax-Element-15">i</script>为结尾的前缀的最长的前缀,使得这个前缀是它的后缀。那么,只要一个前缀出现了一次,它一定就是某一个前缀的后缀,那么这个后缀不停的取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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值