字符串魔法hard(前后缀与贪心)

字符串魔法hard(前后缀与贪心)

链接:https://ac.nowcoder.com/acm/contest/9680/C
来源:牛客网

description:

白浅获得了一个仅由A和B组成的字符串。他可以至多使用一次魔法来改变字符串。 魔法:选择一个子串,满足子串中 A 的数量等于 B
的数量,然后按字典序从小到大排序这个子串,即变成形如AAA…AAABBB…BBB这样的字符串(A和B的数量均与原来的子串相同)。
他想知道,在他至多使用一次魔法后,这个字符串能够出现的最长的字典序不递减的子串的长度为多少。

输入描述: 第一行包含一个整数n,代表字符串的长度。 接下来一行给出一个长度为n的字符串。
1≤n≤200000

输出描述: 输出一行一个正整数表示答案。

Sample:

输入 :
6
AABBAA
输出:
6

解题思路:

  • 首先,我们可以很容易想到拿一个前缀数组去保存AB的数量差,我们这里设A为正,B为负,得到一个prefix_sum数组,prefix_sum[i]=0即代表s到si的子串中A、B数量相等。
  • 对于前缀数组prefix_sum,它可以令我们知道哪些子串是AB数量相等的,具体怎么实现,就是从暴力开始,最后发现其有一定的贪心性质,然后利用一个map实现优化,当然直接一个数组也行,但其下标有正有负所以需要做一些处理,我这里就直接map了。具体看代码注释。
  • 知道了哪些子串AB数量相等后,我们还要知道连续A子串和连续B子串的数据,根据题意得知最后要求的是最长的字典序不递减的子串的长度,所以可以设置一个preA数组sufB数组分别表示以s[i]为末尾的连续A子串长度和以s[i]为开头的连续B子串长度

源码与注释:

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
const int N = 2e5 + 10;
map<int, int> M; //M[k]存放字符串s中前缀和为k的最后下标i, 即最大的i使s[1]-- - s[i]的代数和为k
char s[N];
int n;//字符串长度<=200000
int prefix_sum[N];//以A为正,B为负,存放前缀和,和为0即代表A、B数量相等
int preA[N];//preA[i]表示以s[i]为末尾的连续A子串长度
int sufB[N];//sufB[i]表示以s[i]为开头的连续B子串长度
//preA和sufB的前后缀性质是由题目中最终要求的 字典序不递减 决定的。
int main()
{
	cin >> n >> s + 1;
	//求前缀和prefix_sum
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'A' ) 
			prefix_sum[i] = prefix_sum[i - 1] + 1;
		else 
			prefix_sum[i] = prefix_sum[i - 1] - 1;
	}

	//求preA和sufB以获得连续子串的数据
	for (int i = 1, j = n; i <= n; i++, j--) {
		if (s[i] == 'A')
			preA[i] = preA[i - 1] + 1;
		if (s[j] == 'B')
			sufB[j] = sufB[j + 1] + 1;
	}

	//求出sufB和preA后,按照暴力的思想可以像下面这样遍历,但无奈n的规模有200000,显然是不行的
	//int MAX = -1;
	//for (int i = 0; i <= n; i++) {
	//	for (int j = i + 2; j <= n; j++) {
	//		if (prefix_sum[j] - prefix_sum[i] == 0) {
	//			MAX = max(MAX, j - i + preA[i] + sufB[j + 1]);
	//		}
	//	}
	//}
	//cout << MAX << endl;

	//以下用一个map M存放前缀和的一些信息以做优化
	for (int i = n; i >= 1; i--) {
		if (M[prefix_sum[i]] == 0) {
			M[prefix_sum[i]] = i;//其中M[k]存放字符串s中前缀和为k的最后下标i,即最大的i使s[1]---s[i]的代数和为k
		}
	}
	//这里有点贪心的思维,即在求M时只看最后的下标
	//这样一来,我们知道s[1]---s[i]前缀和为prefix_sum[i],则最长的前缀和为k的子串为s[1]---s[ M[prefix_sum[i]] ],
	//所以有了M,我们就知道以s[i+1]为头的最长的AB数相等的子串为s[i+1]---s[ M[prefix_sum[i]] ],其长度为M[prefix_sum[i]]-i
	//所以我们也不用去做过多的循环了,一轮贪心选择即可完成
	int MAX = -1;
	for (int i = 1; i <= n; i++) {
		//以s[i+1]为头的最长子串及连接两边AB后的长度。
		int nowLength = (M[prefix_sum[i]] - i) + (preA[i] + sufB[M[prefix_sum[i]] + 1]);
		MAX = max(MAX, nowLength);
	}
	cout << MAX << endl;
	return 0;
}


纯源码:

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
const int N = 2e5 + 10;
map<int, int> M; 
char s[N];
int n,prefix_sum[N],preA[N],sufB[N];
int main()
{
	cin >> n >> s + 1;
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'A' ) 
			prefix_sum[i] = prefix_sum[i - 1] + 1;
		else 
			prefix_sum[i] = prefix_sum[i - 1] - 1;
	}
	for (int i = 1, j = n; i <= n; i++, j--) {
		if (s[i] == 'A')
			preA[i] = preA[i - 1] + 1;
		if (s[j] == 'B')
			sufB[j] = sufB[j + 1] + 1;
	}
	for (int i = n; i >= 1; i--) {
		if (M[prefix_sum[i]] == 0) {
			M[prefix_sum[i]] = i;
		}
	}
	int MAX = -1;
	for (int i = 1; i <= n; i++) {
		int nowLength = (M[prefix_sum[i]] - i) + (preA[i] + sufB[M[prefix_sum[i]] + 1]);
		MAX = max(MAX, nowLength);
	}
	cout << MAX << endl;
	return 0;
}

结果:

最后贴一张AC图
在这里插入图片描述


就酱:)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值