hdu3613 Best Reward 扩展kmp or O(n)求最大回文子串

/**
题目:hdu3613 Best Reward
链接:http://acm.hdu.edu.cn/showproblem.php?pid=3613
题意:有一个字符串,把他切成两部分。
如果这部分是回文串,那么他的值为所有字母的权值和。否则这部分值为0;这两部分的值和为该切法的权值。
求最大的切法的权值。
思路:
如果能够判断[0,i],[i,n-1]是一个回文串(0<=i<n)那么就可以枚举i,计算切割位置为i时候两部分的贡献和。
取最大的。
利用O(n)的算法求最长回文子串的做法获得f[i];表示以第i个字符为中心的最长回文子串的长度;
就可以获得l[i],r[i];
l[i]=1表示[0,i]是一个回文串.r[i]=1表示[i,n-1]是一个回文串。

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<set>
#include <iostream>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6+10;///给定的长度
char s[maxn*2], t[maxn];///要乘以2,因为构造了‘*‘
int f[maxn*2];///令f[i] 表示以第i个字符为中心的最长回文子串的长度
int sum[maxn*2], w[30];
int l[maxn*2], r[maxn*2];///l[i]=1表示[0,i]是一个回文串.r[i]=1表示[i,n-1]是一个回文串。
int longest(char *a)///最长回文子串
{
    int z = 0;
    for(int i = 0; a[i]!=‘\0‘; i++){
        s[z++] = a[i];
        s[z++] = ‘*‘;
    }
    z--;
    s[z] = ‘\0‘;
    int j = 0;
    int ans = 1;
    f[0] = 1;
    for(int i = 1; i < z; i++){
        if(f[j]-2*(i-j)<=0) {
            f[i] = 1;
        }else{
            f[i] = min(f[2*j-i],f[j]-2*(i-j));
        }
        int l = i-f[i]/2-1, r = i+f[i]/2+1;
        while(l>=0&&r<z&&s[l]==s[r]){
            l--, r++;
            f[i]+=2;
        }
        if(f[i]/2+i>f[j]/2+j){
            j = i;
        }
        if(f[i]>ans){
            ans = f[i];
        }
    }
    int mas = 0;
    for(int i = 0; i < z; i++){
        if(f[i]==ans){
            int l = i-f[i]/2, r = i+f[i]/2;
            int cnt;
            if(s[l]==‘*‘){
                cnt = f[i]/2;
            }else cnt = f[i]/2+1;
            mas = max(mas,cnt);
        }
    }
    return mas;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    int cas = 1;
    cin>>T;
    while(T--){
        for(int i = 0; i < 26; i++) scanf("%d",&w[i]);
        scanf("%s",t);
        longest(t);
        int n = strlen(s);
        sum[0] = w[s[0]-‘a‘];
        for(int i = 1; i < n; i++){
            if(s[i]==‘*‘) sum[i] = sum[i-1]+0;
            else sum[i] = sum[i-1]+w[s[i]-‘a‘];
        }
        memset(l, 0, sizeof l);
        memset(r, 0, sizeof r);
        for(int i = 0; i < n; i++){
            int L = i-(f[i]-1)/2;
            int R = i+(f[i]-1)/2;
            if(L==0){
                l[R] = 1;
            }
            if(R==n-1){
                r[L] = 1;
            }
        }
        int ans = -INF, ansa, ansb;
        for(int i = 1; i < n-1; i++){
            if(i%2){
                if(l[i-1]){
                    ansa = sum[i-1];
                }else ansa = 0;
                if(r[i+1]){
                    ansb = sum[n-1]-sum[i-1];
                }else ansb = 0;
            }else
            {
                if(l[i-2]){
                    ansa = sum[i-1];
                }else ansa = 0;
                if(r[i]){
                    ansb = sum[n-1]-sum[i-1];
                }else ansb = 0;
            }

            ans = max(ans,ansa+ansb);
        }
        printf("%d\n",ans);
    }
    return 0;
}
/**
题目:hdu3613 Best Reward
链接:http://acm.hdu.edu.cn/showproblem.php?pid=3613
题意:有一个字符串,把他切成两部分。
如果这部分是回文串,那么他的值为所有字母的权值和。否则这部分值为0;这两部分的值和为该切法的权值。
求最大的切法的权值。
思路:
如果能够判断[0,i],[i,n-1]是一个回文串(0<=i<n)那么就可以枚举i,计算切割位置为i时候两部分的贡献和。
取最大的。

扩展kmp的做法。
先计算l[i]=1,表示[0,i]是一个回文串;那么[0,k1]与[k2,i]相等。0是固定的。
获得原串t的反转串s。求extend[i],表示s串从[i,n-1]与原串t的最长公共前缀。
枚举s串的i。那么从[i,n-1]的长度n-i;如果extend[i]*2+1>=n-i那么表示原串t中的[0,n-1-i]是一个回文串即l[n-1-i] = 1;

现在求r[i]=1,表示[i,n-1]是一个回文串;那么[i,k1]与[k2,n-1]相等。n-1是固定的。
求extend[i],表示t串从[i,n-1]与s串的最长公共前缀。
枚举t串的i。那么从[i,n-1]的长度为n-i;如果extend[i]*2+1>=n-i那么表示原串[i,n-1]是一个回文串即r[i] = 1;

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <iostream>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 5e5+10;
char s[maxn], t[maxn];
int f[maxn], Next[maxn], extend[maxn];
int sum[maxn], w[30];
int l[maxn], r[maxn];///l[i]=1表示[0,i]是一个回文串.r[i]=1表示[i,n-1]是一个回文串。
void GetNext(char *T,int* next)
{
    int a=0;
    int Tlen=strlen(T);
    next[0]=Tlen;
    while(a<Tlen-1&&T[a]==T[a+1]) a++;
    next[1]=a;
    a=1;
    for(int k=2;k<Tlen;k++)
    {
        int p=a+next[a]-1,L=next[k-a];
        if((k-1)+L>=p)
        {
            int j=(p-k+1)>0? p-k+1:0;
            while(k+j<Tlen&&T[k+j]==T[j]) j++;
            next[k]=j;
            a=k;
        }
        else next[k]=L;
    }
}

void GetExtend(char *S,char *T,int* next,int* extend)
{
    int a=0;
    GetNext(T,next);
    int Slen=strlen(S);
    int Tlen=strlen(T);
    int MinLen=Slen<Tlen? Slen:Tlen;
    while(a<MinLen&&S[a]==T[a]) a++;
    extend[0]=a;
    a=0;
    for(int k=1;k<Slen;k++)
    {
        int p=a+extend[a]-1,L=next[k-a];
        if((k-1)+L>=p)
        {
            int j=(p-k+1)>0? p-k+1:0;
            while(k+j<Slen&&j<Tlen&&S[k+j]==T[j]) j++;
            extend[k]=j;
            a=k;
        }
        else extend[k]=L;
    }
}
void getlr(int* extend,int *l,int flag,int n)
{
    int len;
    for(int i = 0; i < n; i++){
        len = n-i;
        if(extend[i]*2+1>=len){
            if(flag) l[i] = 1;
            else l[n-1-i] = 1;
        }
    }
}
void test(int *f,int n)
{
    for(int i = 0; i < n; i++){
        printf("f[%d] = %d\n",i,f[i]);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    cin>>T;
    while(T--){
        for(int i = 0; i < 26; i++) scanf("%d",&w[i]);
        scanf("%s",t);
        int n = strlen(t);
        for(int i = 0; i < n; i++) s[n-1-i] = t[i];
        s[n] = ‘\0‘;
        sum[0] = w[t[0]-‘a‘];
        for(int i = 1; i < n; i++){
            sum[i] = sum[i-1]+w[t[i]-‘a‘];
        }
        memset(l, 0, sizeof l);
        memset(r, 0, sizeof r);

        GetExtend(s,t,Next,extend);
        getlr(extend,l,0,n);

        GetExtend(t,s,Next,extend);
        getlr(extend,r,1,n);

        int ans = -INF, ansa, ansb;
        for(int i = 1; i < n; i++){
            if(l[i-1]){
                ansa = sum[i-1];
            }else ansa = 0;
            if(r[i]){
                ansb = sum[n-1]-sum[i-1];
            }else ansb = 0;
            ans = max(ans,ansa+ansb);
        }
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-11-17 14:42:44

hdu3613 Best Reward 扩展kmp or O(n)求最大回文子串的相关文章

最长回文子串 (动态规划法、中心扩展算法)

问题描述: 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 思考: 嗯,回文嘛!就是顺序读取和逆序读取的结果是一样的,那我们可以使用两个for循环来不断的截取给定的字符串S,然后判断截取后的字符串是不是回文串,与此同时,使用一个新的字符串result来保存我们截取到的并且长度最长的回文串. 代码: public String longestPalindrome_reconstructure1(String s){ // 超出时间限制 if (s.lengt

[LeetCode] 647. 回文子串 ☆☆☆(最长子串、动态规划、中心扩展算法)

描述 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串. 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串. 示例 1: 输入: "abc"输出: 3解释: 三个回文子串: "a", "b", "c".示例 2: 输入: "aaa"输出: 6说明: 6个回文子串: "a", "a", "a", "

Best Reward HDU 3613(回文子串Manacher)

题目大意:有一个串(全部由小写字母组成),现在要把它分成两部分,如果分开后的部分是回文串就计算出来它的价值总和,如果不是回文的那么价值就是0,最多能得到的最大价值.   分析:首先的明白这个最大价值有可能是负数,比如下面: -1 -1 -1..... aaa 这样的情况不管怎么分,分出来的串都是回文串,所以得到的最大价值是 -3. 求回文串的算法使用的是Manacher算法,线性的复杂度.   代码如下: =============================================

最长回文子串[中心扩展思想]

#include <iostream> #include<string> using namespace std; string findLongestPalindrome(string &s) { int length=s.size(); int maxlength=0; int start,j,k; for(int i=0;i<length;i++) { if(length%2!=0){//奇偶判断 j=i-1;k=i+1; }else{ j=i;k=i+1; }

Part.5【马拉车&amp;扩展KMP】

Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n).网上对其介绍的资料已经挺多了的,请善用搜索引擎. 而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀[KMP更像是一个自动机] 题目: POJ 1159: Palindrome 求原字符串最少增加几个字符后可变成回文串,相当于求最长回文子序列的长度. 解法:直接求串S和反转串Sr的最长公共子序列. #include <cstdlib> #include <cstdio> #include <

扩展KMP模板

扩展KMP:    给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0 <= i < lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i + z - 1]==B[0 .. z - 1]的最大的z值).    扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串.[算法]    设next[i]为满足B[i..i + z - 1] == B[0..z - 1]的最

浅谈Manacher算法与扩展KMP之间的联系

首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解,网上解法颇多,时间复杂度也不尽相同,这里列述几种常见的解法. 解法一 通过枚举S的子串,然后判断该子串是否为回文,由于S的子串个数大约为,加上每次判断需要的时间,所以总的时间复杂度为,空间复杂度为. bool check(string &S, int left, int right) { while (left < right && S[left]

KMP与扩展KMP

原文转自:http://www.cppblog.com/MatoNo1/archive/2011/04/17/144390.aspx KMP:给出两个字符串A(称为模板串)和B(称为子串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i] (0<=i<lenA),求出A[i]往前和B的前缀匹配的最大匹配长度,记为ex[i](或者说,ex[i]为满足A[i- z+1..i]==B[0..z-1]的最大的z值).KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置

KMP和扩展KMP【转】

这种东西基本上在纸上自己推导一下就能做出来XD 转发注明出处 KMP:给出两个字符串A(称为模板串)和B(称为子串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i] (0<=i<lenA),求出A[i]往前和B的前缀匹配的最大匹配长度,记为ex[i](或者说,ex[i]为满足A[i- z+1..i]==B[0..z-1]的最大的z值).KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置(当 ex[i]=lenB时).[算法]设next[i]为满足B[i-z