HDU 3613 Best Reward(扩展KMP的应用:回文串判断+扩展KMP模板)

Best Reward

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/65536K (Java/Other)

Total Submission(s) : 16   Accepted Submission(s) : 9

Problem Description

After an uphill battle, General Li won a great victory. Now the head of state decide to reward him with honor and treasures for his great exploit.

One of these treasures is a necklace made up of 26 different kinds of gemstones, and the length of the necklace is n. (That is to say: n gemstones are stringed together to constitute this necklace, and each of these gemstones belongs to only one of the 26 kinds.)

In accordance with the classical view, a necklace is valuable if and only if it is a palindrome - the necklace looks the same in either direction. However, the necklace we mentioned above may not a palindrome at the beginning. So the head of state decide to cut the necklace into two part, and then give both of them to General Li.

All gemstones of the same kind has the same value (may be positive or negative because of their quality - some kinds are beautiful while some others may looks just like normal stones). A necklace that is palindrom has value equal to the sum of its gemstones' value. while a necklace that is not palindrom has value zero.

Now the problem is: how to cut the given necklace so that the sum of the two necklaces's value is greatest. Output this value.
 

 

 

Input

The first line of input is a single integer T (1 ≤ T ≤ 10) - the number of test cases. The description of these test cases follows. For each test case, the first line is 26 integers: v[sub]1[/sub], v[sub]2[/sub], ..., v[sub]26[/sub] (-100 ≤ v[sub]i[/sub] ≤ 100, 1 ≤ i ≤ 26), represent the value of gemstones of each kind. The second line of each test case is a string made up of charactor 'a' to 'z'. representing the necklace. Different charactor representing different kinds of gemstones, and the value of 'a' is v[sub]1[/sub], the value of 'b' is v[sub]2[/sub], ..., and so on. The length of the string is no more than 500000.

 

 

Output

Output a single Integer: the maximum value General Li can get from the necklace.

 

 

Sample Input

 

2

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

aba

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

acacac

 

Sample Output

16

 

这题目不能用kmp做的原因是,kmp只能找出包含结尾的最长的回文串,如果里面还有回文串就匹配不出来了。

譬如上面的acacac,用kmp只能匹配出cacac,找不出里面的cac,因为匹配串会完整匹配

acacac

  cacaca

kmp就结束了。

 

本题应用扩展KMP进行回文判断:

方法就是将原串S逆序复制到一个新串T,再分别进行T匹配S和S匹配T

判断回文的方法是若被匹配串的后缀长度(extend[i])==len-i(表示被匹配串i...len-1==匹配串0...extend[i]-1)

说明该部分S[i..len-1]与T[0...extend[i]-1]相等,而有因为S[i..len-1]与T[0...extend[i]-1]逆序,所以同时满足两个条件的只能是

S[i...len-1]回文

这里贴一个大神的博客:http://blog.csdn.net/u013480600/article/details/23041391

还有详解扩展Kmp的PPT:https://wenku.baidu.com/view/8e9ebefb0242a8956bece4b3.html

extend[i]:=S[i..n]与T的最长公共前缀长度

next[i]:T[i..m]与T的最长公共前缀长度

#include<stdio.h>
#include<string.h>
#define MAXN 500010
using namespace std;


int extend1[MAXN],next[MAXN],extend2[MAXN];
char S[MAXN],T[MAXN];
int value[27],sum[MAXN];

void makenext(char T[])
{
    int p,a;
    int len=strlen(T);
    next[0]=len;
    for(int i=1,j=-1;i<len;i++,j--)
    {
        if(j<0||i+next[i-a]>=p)
        {
            if(j<0)
            {
                p=i;
                j=0;
            }

            while(p<len&&j<len&&T[p]==T[j])
            {
                p++;
                j++;
            }

            next[i]=j;
            a=i;
        }
        else
            next[i]=next[i-a];
    }
}

void extend_kmp(char P[],char T[],int extend[])
{
    int plen,tlen,p,a;
    plen=strlen(P);
    tlen=strlen(T);
    makenext(T);
    for(int i=0,j=-1;i<plen;i++,j--)
    {
       if(j<0||i+next[i-a]>=p)
       {
           if(j<0)
           {
               p=i;
               j=0;
           }

           while(p<plen&&j<tlen&&P[p]==T[j])
           {
               p++;
               j++;
           }

           extend[i]=j;
           a=i;
       }
       else
        extend[i]=next[i-a];

    }
}

int main()
{
   int t;
   scanf("%d",&t);
   while(t--)
   {
       for(int i=0;i<26;i++)
        scanf("%d",&value[i]);
       scanf("%s",S);
       int len=strlen(S);
       for(int i=0;i<len;i++)
       {
           T[len-i-1]=S[i];
           if(i==0) sum[i]=value[S[i]-'a'];
           else sum[i]=sum[i-1]+value[S[i]-'a'];
       }
       extend_kmp(S,T,extend1);
       extend_kmp(T,S,extend2);
       int max_=-1;              i表示前半段的长度(都从长度1开始)
       for(int i=1;i<len;i++)   //每一次i(长度)都进行一次尝试,在满足条件的情况下将S分成1..i-1(回文)+i...len-1(回文)
       {                            //相当于在这里切一刀
           int sc=0;
		   //求后缀[i..n-1]
           if(extend1[i]+i==len)   //s的后缀
           {
               sc+=sum[len-1]-sum[i-1];
           }
		   //求前缀[0..i-1]分数
		   //extend[len-j-1]+len-j-1==len,  i=j+1
           if(extend2[len-i]+len-i==len)  //s的前缀,此处i-1表示S中的下标,len-i就表示T中的下标
           {
               sc+=sum[i-1];
           }

           if(max_<sc)
            max_=sc;
       }
       printf("%d\n",max_);
   }
   return 0;
}

 

 

这里有我一开始看的一个大神的扩展KMP 的模板,我自己加了注释,好理解

#include<iostream>
#include<string>
using namespace std;

void GetNext(string T, int next[])
{
    int t_len = T.size();
    next[0] = t_len;
    int a;
    int p;

    for (int i = 1, j = -1; i < t_len; i++, j--)
    {
        if (j < 0 || i + next[i - a] >= p)
        {
            if (j < 0)
                p = i, j = 0;

            while (p < t_len&&T[p] == T[j])
                p++, j++;

            next[i] = j;
            a = i;
        }
        else
            next[i] = next[i - a];
    }
}

/* 求解extend[] */
void GetExtend(string S, string T, int extend[], int next[])
{
    GetNext(T, next);  //得到next
    int a;
    int p;             //记录匹配成功的字符的最远位置p,及起始位置a
    int s_len = S.size();
    int t_len = T.size();

    for (int i = 0, j = -1; i < s_len; i++, j--)  //j即等于p与i的距离,其作用是判断i是否大于p(如果j<0,则i大于p)
    {                                               //j在>0的时候代表T[]现在待匹配的下标,j=min(next[i-a],p-i),又因为p-i<=next[i-a],所以j==p-i
        if (j < 0 || i + next[i - a] >= p)  //i大于p(其实j最小只可以到-1,j<0的写法方便读者理解程序),
        {                                   //或者可以继续比较(之所以使用大于等于而不用等于也是为了方便读者理解程序)
            if (j < 0)                      //i+next[i-a]的情况是可能的extend[i](next[i])值大于了p-i(j),而p之后的部分有没有匹配过,所以要重新匹配(此时j==p-i)
                p = i, j = 0;  //如果i大于p
                                                           //J在代表p-i的同时也表示在接下来的比较中T中待比较的下标,因为T[i..p-1]已经==S[0..p-i-1]
            while (p < s_len&&j < t_len&&S[p] == T[j])  //1.j<0的情况,在这j=0是重新匹配(因为p==i)
                p++, j++;                               //2.next[i-a]+i==p,可得j==next[i-a],们可以直接从S[p]与T[next[i-a]]开始往后匹配

            extend[i] = j;           //j代表每一次S[i...slen]对T的最大前缀和
            a = i;    //此时将T[0]对向S[a],
        }
        else
            extend[i] = next[i - a];   //位置S[i]与P[i-a]对应,求extend[i](i..slem与T前缀最大的公共串)
    }                                  //就相当于next[i-a](i-a...tlen与T前缀最大的公共子串)(因为S[a..p]==T[0...p-a]->S[i+1..p]==T[i-a+2..p-a])
}

int main()
{
    int next[100] = { 0 };
    int extend[100] = { 0 };
    string S = "aaaaabbb";
    string T = "aaaaac";

    GetExtend(S, T, extend, next);

    //打印next和extend
    cout << "next:    " << endl;
    for (int i = 0; i < T.size(); i++)
        cout << next[i] << " ";

    cout << "\nextend:  " << endl;
    for (int i = 0; i < S.size(); i++)
        cout << extend[i] << " ";

    cout << endl;
    return 0;
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值