Balanced Parenthesis on Segments
Given a balanced parenthesis, how many substrings can you invert so that the string remains balanced?
The recently conducted Codeforces Educational Round 166 had an interesting problem D : Invertible Bracket Sequences that dealt with balanced parenthesis. Since I do not have a lot of experience with balanced parenthesis, and educational rounds are meant for you to learn new things, I decided to do some exploration, to solve not only the problem that I was seeing, but other problems on balanced parenthesis that might be trivial to someone with enough experience on the topic.
How do you find out if a string is a balanced parenthesis? If you convert each (
to +1
and )
to -1
, then there are 2 conditions that needs to be satisfied:
- The sum of the entire array should be zero.
- All prefix sum should be non-negative.
Believe it or not, just this piece of information is enough to solve the problem from Edu Round.
The problem asks you to invert the subarray and then check for the validity. If the final string is still a valid parenthesis, then all the prefixes should satisfy condition 2. If the subarray that you invert is
This leads us to the following train of thoughts : How difficult is it to find out if a substring is a valid parenthesis or not? Surely this seems like an easier and very standard problem at first, so if we are not able to come up with the solution instantly, maybe we lack understanding of the basics of balanced parenthesis. Maybe solving this easier version might open up new insights that can help solve the Edu problem?
So I spent some time thinking about it, but could not come up with a solution. So naturally I searched for “balanced parentheses substrings codeforces” and came across this excellent comment by tenshi_kanade. In fact, every little detail that you read in this blog is inspired by that comment. Crazy to think that it’s been 8 years. Time flies!
First, we will attempt to solve the following problem in
Count the number of substrings that are balanced parenthesis.
Just like the original version, what are the conditions required for a substring to be a balanced parenthesis?
- The subarray sum of
should be zero. - After accounting for offset of
, all prefix sum of the subarray should be non-negative.
Let’s count all the good substrings that start at index
In other words, if
How do we populate this
vector<int> safe(n, -1);
for (int l = 0; l < n; l++) {
for (int r = l; r < n; r++) {
int sum = pref[r] - (l ? pref[l - 1] : 0);
if (sum < 0) {
break;
}
safe[l] = r;
}
}
Now, for a fixed
To compute the answer, for each
map<int, vector<int>> psum_to_index;
for (int i = 0; i < n; i++) {
psum_to_index[pref[i]].push_back(i);
}
long long ans = 0;
for (int l = 0; l < n; l++) {
if (safe[l] == -1) {
continue;
}
// To satisfy condition 1, we look for all safe indices which will
// produce a subarray sum of zero.
int need_psum = l ? pref[l - 1] : 0;
auto &vec = psum_to_index[need_psum];
ans += upper_bound(vec.begin(), vec.end(), safe[l]) -
lower_bound(vec.begin(), vec.end(), l);
}
Here is the completed code. You can submit your
The time complexity of this solution is
If you have understood the blog so far, and have upsolved both these problems in
We need a way to calculate
To query the minimum on a segment, we can use segment trees, but if you want to avoid the
segtree<int, op, e> st(pref);
vector<int> safe(n, -1);
for (int l = 0; l < n; l++) {
int low = l, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
// Adding +1 because atcoder's segtree has [close, open) range.
int mn = st.prod(l, mid + 1);
int sum = mn - (l ? pref[l - 1] : 0);
if (sum < 0) {
high = mid - 1;
} else {
low = mid + 1;
safe[l] = mid;
}
}
}
- Optimized Code for balanced substrings. You can submit your solution here.
- Optimized Code for edu problem. You can submit your solution here.
s s s .... s u u u ... u
where s
represents s
and u
represents unsafe. Luckily, the Atcoder Library already provides an interface for this internal binary search (max_right
) so that you don’t have to dive into the details.
int target;
bool f(int v) { return v >= target; }
segtree<int, op, e> st(pref);
vector<int> safe(n, -1);
int offset = 0;
for (int l = 0; l < n; l++) {
// Safe indices have the form
// s, s, s, ..., s, u, u, u, .... u
// s --> safe and u --> unsafe.
//
// Let's find out the rightmost unsafe index via binary search on
// segtree.
// An index is safe if offset + subarray_sum >= 0
// offset + pref[r] - pref[l - 1] >= 0
// pref[r] >= pref[l - 1] - offset
target = (l ? pref[l - 1] : 0) - offset;
int unsafe = st.max_right<f>(l);
unsafe--;
if (l <= unsafe) {
safe[l] = unsafe;
}
offset += (str[l] == '(') ? 1 : -1;
}
- Optimized Code for internal Binary Search for edu problem. You can submit your solution here.
If you need help or clarifications, feel free to reach out to me on the following platforms.