Best Reward

Problem's Link:   http://acm.hdu.edu.cn/showproblem.php?pid=3613


Mean:

给你一个字符串,每个字符都有一个权值(可能为负),你需要将这个字符串分成两个子串,使得这两个子串的价值之和最大。一个子串价值的计算方法:如果这个子串是回文串,那么价值就是这个子串所有字符权值之和;否则价值为0。

analyse:

扩展KMP算法运用。
总体思路:
找出所有包含第一个字母的回文串和包含最后一个字母的回文串,然后O(n)扫一遍,每次判断第i个字母之前(包含第i个字母)的子串是否是回文,以及从第i个字母后的子串是否是回文,然后计算出答案,取最大值。
具体做法:
假设输入的字符串是"abcda"
构造串s1="abcda#adcba"
求s1的Next数组,得到了包含第一个字母的回文串的位置;
构造串s2="adcba#abcda"
求s2的Next数组,得到了包含最后一个字母的回文串的位置;
用两个flag数组标记这些位置,然后扫一遍就得答案了。
中间加一个'#'并后接反串的目的是:当整个串都是回文的时候能够被Next数组记录下。

Time complexity: O(nlogn)

Source code: 

第一遍写,不够优化:

/*
* this code is made by crazyacking
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <string>
#include <stack>
#include <cmath>
#include <set>
#include <map>
#include <cstdlib>
#include <climits>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cstring>
#define MAXN 500010*2
#define LL long long
using namespace std;
int len;
int Next[MAXN],ne[MAXN];
int sum[MAXN];
vector<int> val;
bool flag1[MAXN],flag2[MAXN];
char s[MAXN],s1[MAXN],s2[MAXN],sr[MAXN];
void get_sum()
{
sum[]=val[s[]-'a'];
for(int i=;i<len;++i)
sum[i]=sum[i-]+val[s[i]-'a'];
}
void get_s1()
{
strcpy(s1,s);
s1[len]='#';
s1[len+]='\0';
strcat(s1,sr);
}
void get_s2()
{
strcpy(s2,sr);
s2[len]='#';
s2[len+]='\0';
strcat(s2,s);
} void get_Next(char s[])
{
Next[]=;
int s_len=strlen(s);
for(int i=,k=;i<s_len;++i)
{
while(k!= && s[i]!=s[k])
k=Next[k-];
if(s[i]==s[k]) k++;
Next[i]=k;
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie();
int Cas;
cin>>Cas;
while(Cas--)
{
val.clear();
int cnt=,t;
while(cnt--)
{
cin>>t,val.push_back(t);
}
scanf("%s",s);
len=strlen(s);
if(strlen(s)==)
{
printf("%d\n",val[s[]-'a']);continue;
}
get_sum();
strcpy(sr,s);
strrev(sr);
get_s1();
get_s2();
memset(flag1,,sizeof flag1);
memset(flag2,,sizeof flag2);
get_Next(s1);
int k=Next[*len];
while(k!=)
{
flag1[k-]=;
k=Next[k-];
}
get_Next(s2);
k=Next[*len];
while(k!=)
{
flag2[k-]=;
k=Next[k-];
}
reverse(flag2,flag2+len);
long long ans=INT_MIN;
long long tmp=;
for(int i=;i<len-;++i)
{
tmp=;
if(flag1[i])
{
tmp+=sum[i];
}
if(flag2[i+])
{
tmp=tmp+(sum[len-]-sum[i]);
}
if(tmp>ans)
ans=tmp; }
cout<<ans<<endl; }
return ;
}
/* */

优化后的代码:

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-05-07-16.26
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define LL long long
#define ULL unsigned long long
using namespace std;
const int MAXN=;
int val[],Next[MAXN*],sum[MAXN];
char s[MAXN],s1[MAXN*];
bool flag[][MAXN];
void get_sum()
{
int len=strlen(s);
sum[]=val[s[]-'a'];
for(int i=;i<len;++i)
sum[i]=sum[i-]+val[s[i]-'a'];
} void get_Next(char ss[])
{
int len=strlen(ss);
Next[]=;
int k=;
for(int i=;i<len;++i)
{
while(k!= && ss[i]!=ss[k])
k=Next[k-];
if(ss[i]==ss[k]) k++;
Next[i]=k;
}
} void get_flag(int x)
{
strcpy(s1,s);
int len=strlen(s);
s1[len]='#';
strrev(s);
strcat(s1+len+,s);
get_Next(s1);
len=strlen(s1);
int k=Next[len-];
while(k!=)
{
flag[x][k-]=;
k=Next[k-];
}
memset(s1,,sizeof s1);
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie();
int Cas;
scanf("%d",&Cas);
while(Cas--)
{
for(int i=;i<;++i)
scanf("%d",&val[i]);
scanf("%s",s);
if(strlen(s)==)
{
printf("%d\n",val[s[]-'a']);continue;
}
get_sum();
memset(flag,,sizeof flag);
get_flag();
get_flag();
int len=strlen(s);
reverse(flag[],flag[]+len);
long long ans=LLONG_MIN,tmp;
for(int i=;i<len-;++i)
{
tmp=;
tmp=(flag[][i]?sum[i]:)+(flag[][i+]?sum[len-]-sum[i]:);
ans=ans>tmp?ans:tmp;
}
printf("%lld\n",ans);
}
return ;
}
/* */
05-04 03:50