首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/883/B
来源:牛客网
涉及:思维,前缀和
点击这里回到2019牛客暑期多校训练营解题—目录贴
题目如下
一道比较简单 的思维题
此题让我们找到原字符串中‘0’和‘1’数量相同的子串和子序列。
注意子串在原字符串是连续的;而子序列在原字符串是可以不连续的,但是顺序不能改变
先讨论最长的‘0’‘1’数量相同的子序列:
这个很简单,数一数原字符串‘0’和‘1’的数量,假设‘0’的数量为,‘1’的数量为
那么满足条件的子序列最长的长度为
再讨论最长的‘0’‘1’数量相同的子串:
此时有一种方法:将原字符串的‘0’改为‘-1’,然后求原字符串的前缀和。
由于一个‘1’会和另外一个‘-1’抵消为0,故前缀和中如果存在两个相同的数,说明就存在一个‘0’‘1’相同的子串:
解释:
0与0之间最长隔了2个单位(红色)
-1与-1之间最长隔了4个单位(绿色)
-2与-2之间最长隔了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;
}