题目链接
题目大意:
给你一个长度为 n ( n ≤ 1 e 5 ) n(n \leq 1e5) n(n≤1e5)只含有 0 , 2 0,2 0,2的字符串。每个数字只能用一次。让你找出最多的互相不重复的子序列 2020 2020 2020.
例如:
8
20220200
2
题目思路:
2019湘潭邀请赛有道简单版,这道复杂一点。
如果四个数字互不相同。直接贪心即可。这里的问题就在于数字有重复。那么《凑满一个是一个》这种贪心会错.比如上面那个。
换一种贪心思路:
假设让我们找出
x
x
x个子序列。那么可以这么做:
先找出前
x
x
x个2.
再对每个
2
2
2.找后面最近的一个没使用过的0匹配成x个子序列
20
20
20.
再对每个
20
20
20.找后面最近的一个没使用过的2匹配成x个子序列
202
202
202
再对每个
202
202
202.找后面最近的一个没使用过的0匹配成x个子序列
2020
2020
2020.
如果这个过程能够跑完,即合法,否则不行。
然后答案有单调性,所以直接二分答案即可。
正确性:让每个子序列尽量分布于前面显然最优
AC代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
int n;
string a;
string b = "2020";
int st[maxn][4] , bk[maxn];
int check (int x)
{
for (int i = 1 ; i <= n ; i++) memset(st[i] , 0 , sizeof st[i]) , bk[i] = 0;
int num = x;
for (int i = 1 ; i <= n && num; i++) {
if (a[i] != b[0]) continue;
st[i][0] = 1;
bk[i] = 1;
num--;
}
if (num) return false;
num = x;
int s = 0;
for (int i = 1 ; i <= n && num ; i++){
s += st[i][0];
if (s == 0) continue;
if (a[i] != b[1]) continue;
if (bk[i]) continue;
st[i][1] = 1;
bk[i] = 1;
num--;
s--;
}
if (num) return false;
num = x;
s = 0;
for (int i = 1 ; i <= n && num ; i++){
s += st[i][1];
if (s == 0) continue;
if (a[i] != b[2]) continue;
if (bk[i]) continue;
st[i][2] = 1;
bk[i] = 1;
num--;
s--;
}
if (num) return false;
num = x;
s = 0;
for (int i = 1 ; i <= n && num ; i++){
s += st[i][2];
if (s == 0) continue;
if (a[i] != b[3]) continue;
if (bk[i]) continue;
st[i][3] = 1;
bk[i] = 1;
num--;
s--;
}
if (num) return false;
return true;
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> n){
cin >> a;
a = '#' + a;
int l = 0 , r = n / 4;
while (l <= r){
int mid = l + r >> 1;
if (check(mid)) l = mid + 1;
else r = mid - 1;
}
cout << r << endl;
}
return 0;
}