Educational Codeforces Round 128 (Rated for Div. 2)


比赛链接:

https://codeforces.com/contest/1680

C. Binary String

题目大意:

给定一个二进制字符串,可以从头部和尾部删除一个子串,最后的价值为删除的 1 的数量和剩下的 0 的数量的最大值,问这个价值最小是多少。

思路:

考虑暴力做法,枚举剩下的字符串的起点和终点,\(O(n^2)\),超时。
如果固定了左端点,可以发现右端点的移动对答案的影响是单调的,当右端点向右移动时,剩下的 0 的数量增加,删除的 1 的数量减少,向左移动时,剩下的 0 的数量减少,删除的 1 的数量增加。可以通过二分求答案。

也可以固定右端点,通过双指针移动,当左指针向右移动时,删除的 1 的数量增加,剩下的 0 的数量减少,右指针向右移动时,删除的 1 的数量减少,剩下的 0 的数量增加,当二者达到平衡的时候,就是值最小的时候。

二分

#include
using namespace std;
int T;
string s;
void solve(){
	cin >> s;
	int n = s.size(), ans = n + 1, sum = 0;
	vector  cnt0(n + 1, 0), cnt1(n + 1, 0);
	for (int i = 0; i < n; i ++ ){
		cnt0[i + 1] = cnt0[i] + (s[i] == '0');
		cnt1[i + 1] = cnt1[i] + (s[i] == '1');
		sum += (s[i] == '1');
	}
	ans = min(ans, cnt1[n] - cnt1[0]);
	for (int i = 0; i < n; i ++ ){
		int l = i, r = n;
		while (l < r){
			int mid = l + r >> 1;
			int a = sum - (cnt1[mid + 1] - cnt1[i]), b = cnt0[mid + 1] - cnt0[i];
			if (a >= b){
				ans = min(ans, max(a, b));
				l = mid + 1;
			}
			else r = mid;
		}
	}
	cout << ans << "\n";
}
int main(){
	cin >> T;
	while (T -- )
		solve();
	return 0;
}

双指针

#include
using namespace std;
int T;
string s;
void solve(){
	cin >> s;
	int n = s.size(), ans = n + 1, sum = 0;
	vector  cnt0(n + 1, 0), cnt1(n + 1, 0);
	for (int i = 0; i < n; i ++ ){
		cnt0[i + 1] = cnt0[i] + (s[i] == '0');
		cnt1[i + 1] = cnt1[i] + (s[i] == '1');
		sum += (s[i] == '1');
	}
	ans = min(ans, cnt1[n] - cnt1[0]);
	for (int l = 0, r = 0; r < n; ){
		while (l < r && sum - (cnt1[r + 1] - cnt1[l]) < cnt0[r + 1] - cnt0[l])
			l ++ ;
		ans = min(ans, max(sum - (cnt1[r + 1] - cnt1[l]), cnt0[r + 1] - cnt0[l]));
		r ++ ;
	}
	cout << ans << "\n";
}
int main(){
	cin >> T;
	while (T -- )
		solve();
	return 0;
}