2019牛客暑期多校训练营(第三场)----B-Crazy Binary String

首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/883/B
来源:牛客网
涉及:思维,前缀和

点击这里回到2019牛客暑期多校训练营解题—目录贴


题目如下
在这里插入图片描述
在这里插入图片描述
一道比较简单 的思维题

此题让我们找到原字符串中‘0’和‘1’数量相同的子串和子序列。

注意子串在原字符串是连续的;而子序列在原字符串是可以不连续的,但是顺序不能改变


先讨论最长的‘0’‘1’数量相同的子序列:
这个很简单,数一数原字符串‘0’和‘1’的数量,假设‘0’的数量为sum0sum0,‘1’的数量为sum1sum1

那么满足条件的子序列最长的长度为
2min(sum0,sum1)2*min(sum0,sum1)


再讨论最长的‘0’‘1’数量相同的子串:
此时有一种方法:将原字符串的‘0’改为‘-1’,然后求原字符串的前缀和。
在这里插入图片描述
由于一个‘1’会和另外一个‘-1’抵消为0,故前缀和中如果存在两个相同的数,说明就存在一个‘0’‘1’相同的子串:
在这里插入图片描述
解释:
0与0之间最长隔了2个单位(红色)
-1与-1之间最长隔了4个单位(绿色)
-2与-2之间最长隔了4个单位(蓝色)
所以最长满足条件子串为max(2,4,4)=4max(2,4,4)=4

通过上面的方法,可以给前缀和每一个数标上它的位置,然后以主键为前缀和数值,次主键为每个数的位置进行从小到大排序,求得每两个相同的数最长隔的单位长度,其中距离最大的单位长度就是答案。具体可以看代码


例子上面举了


代码如下:

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

int n, word;//n为题目所给变量,word循环存字符串每一个值
struct Num {//前缀和结构体
	int sum;//前缀和的值
	int place;//这个值的位置
};
Num num[100005];//前缀和结构体数组
int sum1 = 0, sum0 = 0;//sum1为'1'的数量,sum0为'0'的数量
int ans1, ans2 = 0;//ans1为最长子序列长度的一半,ans2位最长子串长度

bool comp(Num a, Num b) {//以sum为主键,place为次主键
	if(a.sum == b.sum)	return a.place < b.place;
	else return a.sum < b.sum;
}

int main() {
	scanf("%d", &n);
	num[0].sum = num[0].place = 0;//先将num[0]和num[n+1]初始化
	num[n+1].sum = 100001;
	for(int i = 1; i <= n; i++) {
		scanf("%1d", &word);//循环输入每一个字符
		if(word == 1) {
			num[i].sum = (i - 1 == 0? 0: num[i-1].sum) + 1;//更新前缀和数组
			sum1++;//记录字符'1'个数
		}
		else{
			num[i].sum = (i - 1 == 0? 0: num[i-1].sum) - 1;//更新前缀和数组
			sum0++;//记录字符'0'个数
		}
		num[i].place = i;//记录每个值的位置
	}
	ans1 = min(sum1, sum0);//最长子序列长度的一半
	sort(num, num + n + 1, comp); //按照上述方法排序
	int flag = 0; //记录每一串前缀和数相同的第一个数的位置
	for(int i = 1; i <= n; i++) {
		if(num[i].sum != num[i-1].sum) {//记录相同数开始位置
			flag = i;
		}
		else if(num[i].sum != num[i+1].sum) {//遍历到相同数最后一个位置
			ans2 = max(ans2, num[i].place - num[flag].place);//更新答案
		}
	}
	printf("%d %d", ans2, ans1*2);
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章