PROBLEM LINK:
Practice
Contest: Division 1
Contest: Division 2
Contest: Division 3
Contest: Division 4
Author: iceknight1093
Tester: apoorv_me
Editorialist: iceknight1093
DIFFICULTY:
TBD
PREREQUISITES:
Prefix sums
PROBLEM:
You have a string S, containing only the characters a
, b
, and c
.
In one move, you can choose a subsequence of S that equals 'abc'
, and delete either the a
or the c
from it.
Find the minimum possible number of operations that can be performed on S, so that it’s impossible to perform any more.
EXPLANATION:
The number of operations performed is equal to the number of characters deleted from S, so let’s look at it from that perspective, and find the minimum number of characters that must be deleted.
Clearly, every b
in S will remain.
Suppose S_i = b
. Then, for there to not be any 'abc'
subsequence involving this b
in the final string,
- Every
a
that occurs before index i should be deleted, or - Every
c
that occurs after index i should be deleted.
Let the indices of b
’s in S be b_1, b_2, \ldots, b_k in ascending order.
We’ll call index b_i an a
-type if every a
before it is deleted, and a c
-type if every c
after it is deleted (it’s never optimal for it to be both).
Observe that no matter how we perform operations, the set of a
-type indices will form a prefix of [b_1, \ldots, b_k], and the c
-types will form a suffix.
After all, if all the a
’s before b_i are deleted, surely all the a
’s before each of b_1, b_2, \ldots, b_{i-1} will also be deleted.
Let’s utilize this observation: for 0 \leq i \leq k, let’s fix the a
-type indices to be b_1, \ldots, b_i, and try to find the minimum number of operations we need to do so.
For convenience, we define b_0 := 0 and b_{k+1} := N+1, which helps deal with border cases.
Observe that:
- It’s enough to delete every
a
before index b_i: this automatically makes all of b_1, b_2, \ldots, b_i bea
-types. - The indices \gt b_i should be
c
-types.
For this, it’s similarly enough to delete everyc
after index b_{i+1}. - So, the number of moves required equals the number of
a
’s before b_i, plus the number ofc
’s after b_{i+1}.
Both these quantities can be found in \mathcal{O}(1) time with the help of prefix/suffix sums.
We now have a lower bound on the answer: take the minimum number of operations across all k+1 choices of i above.
It’s not hard to see that operations can be performed in such a way that we reach this bound too, which thus makes it the answer.
Proof
In an optimal solution, suppose we want to make b_1, \ldots, b_i into a
-type indices, and b_{i+1}, \ldots, b_k into c
-type indices.
If multiple i attain the minimum, choose the largest one among them.
Now, if there exists an occurrence of c
after index b_i, we can use that occurrence to delete every a
before b_i. Then,
- If i = k, we’re done since there are no occurrences of
c
to the right of b_{k+1} = N+1. - Otherwise, since we chose the largest optimal index, there must be an
a
between indices b_i and b_{i+1} - otherwise, b_{i+1} would also be optimal (and we know it isn’t).
So, using thisa
, everyc
after b_{i+1} can be deleted.
This leaves only the case of when there are no c
’s after index b_i.
Here,
- If S itself contains no
c
’s, then the answer is 0 anyway. - Otherwise, let the last
c
in S be between indices b_j and b_{j+1}. Note that j \lt i.
Further, it’s still optimal to choose b_j instead of b_i, since either way noc
’s must be deleted and instead we may delete lessa
’s.
Choosing b_j now gives us an occurrence ofc
to the right to work with, and so we’re done.
TIME COMPLEXITY:
\mathcal{O}(N) per testcase.
CODE:
Tester's code (C++)
#include<bits/stdc++.h>
using namespace std;
int32_t main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int sumN = 0;
auto __solve_testcase = [&](int test) {
int N; cin >> N;
string S; cin >> S;
S = "bb" + S + "bb"; N += 4;
vector<int> ind, pfa(N + 1), pfc(N + 1);
for (int i = 0 ; i < N ; ++i) {
if(S[i] == 'b') ind.push_back(i);
pfa[i + 1] = pfa[i] + (S[i] == 'a');
pfc[i + 1] = pfc[i] + (S[i] == 'c');
}
int ans = N;
for(int i = 0 ; i + 1 < (int)ind.size() ; ++i) {
ans = min(ans, pfa[ind[i]] + pfc[N] - pfc[ind[i + 1]]);
}
cout << ans << '\n';
};
int NumTest = 1;
cin >> NumTest;
for(int testno = 1; testno <= NumTest ; ++testno) {
__solve_testcase(testno);
}
assert(sumN <= (int)3e5);
return 0;
}
Editorialist's code (Python)
for _ in range(int(input())):
n = int(input())
s = 'b' + input() + 'b'
n += 2
b = []
for i in range(n):
if s[i] == 'b': b.append(i)
prefa = [0]*n
sufc = [0]*n
for i in range(n):
if s[i] == 'a': prefa[i] = 1
if i > 0: prefa[i] += prefa[i-1]
for i in reversed(range(n)):
if s[i] == 'c': sufc[i] = 1
if i+1 < n: sufc[i] += sufc[i+1]
ans = n
for i in range(len(b)-1):
x, y = b[i], b[i+1]
ans = min(ans, prefa[x] + sufc[y])
print(ans)