题目:
题目描述:
给你一串 01字符串 s ,你是否能找到一个 01字符串 t ,使得达到两个目标:
① t 串与 s 串在任意 l 和 r 区间内()区间位置的最长非递减子序列(前 0 后 1 )的长度与是一样的
② t 串要拥有尽可能多的 0
思路:
不难想到如果 t 串就是 s 串本身的就能很好的满足目标 ①
但是为了目标 ② ,我们要尽可能的把部分 1 转化成 0 ,但又不改变所有区间的最长非递减子序列的长度
我们怎么才能讲所有区间考虑完全?我们可以从后往前遍历,精准探讨每一个位置作为左区间的情况下去广泛分析有区间的情况。遍历到 0 自然不用动,当我们遍历到 1 的时候, 什么情况下我们可以把这个 1 变成 0 ?当且仅当后面的 1 个数等于 0 的个数的时候才可以,因为在这种情况下:
①要么是位于最后,后面什么都没有
②要么是后面的串中 1 都在 0 前面,且二者个数相等(之前已经转变的位置不考虑不在内,之后解释)
所以在这个时候,会达成一种平衡状态,即,无论我这个位置是 1 还是 0 ,这个位置以及右侧的最长非递减子序列都一样长。
由图可见,你将 1 转变为 0 不会影响这个位置右侧的最长非递减子序列。并且转变之后,这个位置以及其右半部分的区域我们就可以扔掉不讨论了,因为无论在此之前前面的最后一位是 1 还是 0 ,后面的最长非递减子序列都是一样长的。(神奇吧)
所以我们现在只用从后往前遍历,如果当前后面 1 和 0 个数相等的时候(之前的转换位置不统计)再遇见 1 就可以把这个 1 转换为 0
思路有了,具体操作请看AC代码
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
char b[N];
int main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
string a;
cin >> a;
int n = a.size();
int cnt = 0;
for (int i = n - 1; i >= 0; i--)
{
if (a[i] == '0')
{
cnt++;
b[i] = '0';
}
else if (cnt)
{
cnt--;
b[i] = '1';
}
else
b[i] = '0';
}
for (int i = 0; i <= n - 1; i++)
cout << b[i];
return 0;
}