zoukankan      html  css  js  c++  java
  • 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]!=''; i++){
            s[z++] = a[i];
            s[z++] = '*';
        }
        z--;
        s[z] = '';
        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
    ",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
    ",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] = '';
            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
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    bzoj1618 / P2918 [USACO08NOV]买干草Buying Hay(完全背包)
    bzoj1617 / P2904 [USACO08MAR]跨河River Crossing
    bzoj1615 / P2903 [USACO08MAR]麻烦的干草打包机The Loathesome Hay Baler
    bzoj1613 / P1353 [USACO08JAN]跑步Running
    bzoj1612 / P2419 [USACO08JAN]牛大赛Cow Contest(Floyd)
    bzoj1611 / P2895 [USACO08FEB]流星雨Meteor Shower
    bzoj1610 / P2665 [USACO08FEB]连线游戏Game of Lines
    bzoj1609 / P2896 [USACO08FEB]一起吃饭Eating Together(最长不降子序列)
    bzoj1606 / P2925 [USACO08DEC]干草出售Hay For Sale(01背包)
    [bzoj1041][HAOI2008]圆上的整点
  • 原文地址:https://www.cnblogs.com/xiaochaoqun/p/7353286.html
Copyright © 2011-2022 走看看