51Nod 1277 字符串中的最大值 ( KMP && DP )

题意 : 一个字符串的前缀是指包含该字符第一个字母的连续子串,例如: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,是所有前缀中最大的。
 
分析 : 假设现在有一个位置pos,其前缀已经出现一次即 0~(pos-1) 这个前缀已经出现了一次,你现在考虑一下 Next[pos] 是个什么东西?其实就是包含在 0~(pos-1) 这个前缀里面的前缀(注意!前缀的意思是针对整个字符串而言,并非 0~(pos-1) 这个子串),也就是如果我们能够知道一个前缀出现的次数,那么包含在这个前缀里面的前缀也应当又出现了一次,如何找到这个包含在前缀里面的前缀呢?刚刚已经说过,Next数组就是干这个活的,所以对于这一道题,我们只要让字符串去跑一遍KMP得到Next数组,然后对每一个前缀出现的次数都叠加到其包含的前缀当中去,即状态转移方程 d[Next[i]] += d[i] ( d[i]代表长度为 i 的前缀出现的次数),这里注意了,整个过程应该是逆推的!也就是 i 应该从字符串总长推到 1 即先计算最长的前缀,想想为什么?在这个过程不断更新最大值答案即可
 
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int INF  = 0x3f3f3f3f;
char mo[maxn];
int Next[maxn];
long long d[maxn];
int moL;
inline void GetNext()
{
    int i = 0, j = -1;
    Next[i] = j;
    while(i < moL){
        while(j!=-1 && mo[i]!=mo[j]) j = Next[j];
        Next[++i] = ++j;
    }
}
inline void PrintAns()
{
    GetNext();
    memset(d, 0, sizeof(d));
    long long ans = -INF;
    for(int i=moL; i>0; i--){
        d[i]++;///长度为 i 的前缀出现肯定至少一次
        d[Next[i]] += d[i];///对包含在其中的前缀进行值的叠加
        ans = max(ans, d[i]*i);///维护更新答案
    }
    printf("%lld\n", ans);
}
int main(void)
{
    scanf("%s", mo);
    moL = strlen(mo);
    PrintAns();
    return 0;
}
View Code

 

瞎 : 类似这种前缀啊!后缀啊!什么求什么和出现次数有关的,就要灵敏一点想到KMP啦!

posted @ 2017-09-22 21:06  qwerity  阅读(254)  评论(0编辑  收藏  举报