HDU3613【Best Reward】(回文串、马拉车算法的应用)

题目地址:HDU3613【Best Reward】

题目描述:
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.

输入描述:
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: v1, v2, …, v26 (-100 ≤ vi ≤ 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 v1, the value of ‘b’ is v2, …, and so on. The length of the string is no more than 500000.

输出描述:
Output a single Integer: the maximum value General Li can get from the necklace.

输入样例:

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

输出样例:

1
6

题意:
T组输入
每组先输入26个数字 vi,代表26个字母各自的价值
再输入一行字符串
将字符串切割成两个子串
若子串为回文串,则子串价值为各字母价值之和;否则价值为0
求两子串价值相加的最大值

数据范围:
1 ≤ T ≤ 10,-100 ≤ vi ≤ 100, 1 ≤ i ≤ 26,每个字符串长度不超过500000

思路:
首先,用Manacher算法求出以每个字符为中心的回文串的长度;Manacher算法模板
然后,枚举切割点,得到两个子串,
由此确定每个子串的中心点;
检查以该子串的中心点作为中心点的回文串的长度,
如果回文串的长度等于该子串的长度,
则该子串的所有字符的价值之和加入两个项链的价值之和;
并对所有的两个项链的价值之和求最大值

代码:

#include<stdio.h> 
#include<iostream> 
#include<string.h> 
#include<algorithm> 
#define met(a) memset(a,0,sizeof(a));
#define M 500050 
using namespace std; 
typedef long long ll;
char str[M],str_new[2*M];		//原串和辅助串
int p[2*M],sum[M],len;			//p[i]表示以第 i 个字符为中心的最长回文的半径;字符串长度 len
int init()						// 构造辅助串
{
	len = strlen(str);			//计算字符串长度
	str_new[0] = '$';			//辅助串的首字符
	str_new[1] = '#';			//辅助串的间隔字符
	int j = 2;
	for(int i = 0;i < len;i++)	//逐个字符地构造辅助串
	{
		str_new[j++] = str[i];
		str_new[j++] = '#';
	}
	str_new[j] = '\0';		//辅助串的尾字符
	return j;
}
void Manacher()				//计算p[i]
{
	len = init();
	memset(p,0,sizeof(p));//p[i]表示以第 i 个字符为中心的最长回文的半径,所有最长回文的半径初始化为 0 
	int mx=0,di;			//以 id 为中心的最长回文的右边界为 mx,即 mx=id+p[id],mx 和最长回文的长度 ans 初始化为 0
	for(int i = 1;i < len;i++)	//枚举每一个可能的中心字符
	{
		//根据 i 位置在 mx 位置的左侧还是右侧,调整以最长回文的半径的最长回文半径的初始值
		p[i] = mx > i ? min(p[2 * di - i], mx - i) : 1;
		while(str_new[i-p[i]] == str_new[i+p[i]])p[i]++; //以 i 位置为中心计算最长回文半径 p[i]
		if(p[i]+i > mx)//若以 i 为中心的右边界大于 mx,则中心 id 调整为 i,重新计算右边界 mx
			di = i,mx = p[i]+i;
	}
	return;
}

int main()
{
	int t,v[30];
	scanf("%d",&t);
	while(t--)
	{
		memset(p,0,sizeof(p));
		for(int i = 0;i < 26;i++) scanf("%d",&v[i]);//获取每个字母的价值 
		scanf("%s",str);
		sum[0] = v[str[0]-'a'];
		for(int i = 1;str[i];i++)//计算原串str中以每个字符为尾的前缀价值和序列sum[]
			sum[i] = sum[i-1]+v[str[i]-'a'];
		Manacher();//Manacher算法求出以每个字符为中心的回文串半径序列p[] 
		int mx = 0;//mx用来记录最大值 
		len = strlen(str);
		for(int i = 0;i < len-1;i++)//枚举原字符串中每个可能的切割点i
		{
			int tmp = 0,num; //tmp用来更新最大值
			num = p[i+2]-1;//左子串,在辅助串中的中心点为第i+2个字符
			if(num == i+1) tmp += sum[i];
			num = p[i+len+2]-1;//右子串,在辅助串中的中心点为第i+L+2个字符
			if(num == len-i-1) tmp += sum[len-1]-sum[i];
			if(tmp > mx) mx = tmp;
		}
		printf("%d\n",mx); 
	}
	return 0;
}

注:
主函数中

num = p[i+2]-1;//左子串,在辅助串中的中心点为第i+2个字符
if(num == i+1) tmp += sum[i];
num = p[i+len+2]-1;//右子串,在辅助串中的中心点为第i+L+2个字符
if(num == len-i-1) tmp += sum[len-1]-sum[i];

可看做是

if(p[i+2] == i+2) tmp += sum[i]
if(p[i+len+2] == len-i-2) tmp += sum[len-1]-sum[i];

即回文串长度等于子串长度

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温柔说给风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值