码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2017-08-13 15:20:20      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:space   void   set   ace   回文   char   std   stream   log   

/**
题目: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;
}

 

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

标签:space   void   set   ace   回文   char   std   stream   log   

原文地址:http://www.cnblogs.com/xiaochaoqun/p/7353286.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!