PROBLEM LINK:
Practice
Contest: Division 1
Contest: Division 2
Contest: Division 3
Contest: Division 4
Author: pols_agyi_pols
Tester & Editorialist: iceknight1093
DIFFICULTY:
1699
PREREQUISITES:
Observation
PROBLEM:
You’re given a string S of length N.
In one move, you can delete a substring of length 3 from it.
Is it possible to obtain a non-empty palindrome by performing this operation several times?
EXPLANATION:
Let’s think about this problem in reverse: suppose we end up with a palindrome — can we say anything about it?
In particular, suppose we end up with a palindrome P of length K. Then,
- If K \geq 7, we can use our operation to delete the first 3 and last 3 characters of P to obtain a shorter one, of length K - 6.
This means it’s enough to check if it’s possible to obtain a palindrome of length \leq 6. - In fact, we can reduce this even further:
- If K = 4, delete the first three characters and we’re left with a length-1 string; which is a palindrome.
- If K = 5, delete the middle three characters to obtain a length-2 palindrome.
- If K = 6, delete the second, third, fourth characters to obtain a length-3 palindrome.
- Together, this tells us that we only really need to check whether a palindrome of length \leq 3 can be obtained.
Now, notice that the length of the string doesn’t change modulo 3, since we delete three characters at a time.
So, whether we need to check for length = 1, 2, or 3 is uniquely determined by the value of N modulo 3.
Let’s look at what happens for different values of N. There are three cases here.
Case 1
Case 1: N = 3x+1 for some x
Here, it’s always possible to obtain a palindrome: keep the first character and delete the remaining ones.
Case 2
Case 2: N = 3x+2 for some x.
Here, we need to check whether a length-2 palindrome can exist.
Suppose i and j are the indices remaining in the end, and everything else is deleted. Then,
- We should have S_i = S_j.
- Everything before i has to be deleted, so the number of characters before i should be a multiple of 3.
That is, i = 3k_1 + 1 for some integer k_1, or i \bmod 3 = 1. - Everything after j has to be deleted, so the number of characters after j should also be a multiple of 3.
That is, j \bmod 3 = N\bmod 3. - If the above two conditions are satisfied, the condition N = 3x+2 means that the number of
characters between i and j will also be a multiple of 3, so everything inbetween can be deleted.
Checking whether some valid i and j exist isn’t too hard.
- First, fix a character c, and consider only positions containing c.
- Find the leftmost i such that S_i = c and i\bmod 3 = 1.
- Find the rightmost j such that S_j = c and j\bmod 3 = N\bmod 3.
- If i \leq j, we’ve found a valid pair for this character.
This can be implemented in \mathcal{O}(N), though straightforward \mathcal{O}(26\cdot N) is also fast enough.
If a valid pair is found for any character, the answer is Yes
.
If the check fails for all characters, the answer is No
.
Case 3
Case 3: N = 3x for some x.
This is in fact basically the same as case 2 above.
Here, we need to check whether a valid length-3 palindrome can be formed.
However, the middle character doesn’t matter much - it can be anything.
So, once again if we’re able to find indices i and j such that:
- i \leq j and S_i = S_j
- Everything before i and everything after j can be deleted
we’ll be done, because the number of characters between such i and j will be 1 modulo 3; meaning we can delete all but one of them.
The exact same \mathcal{O}(26\cdot N) algorithm to compute i and j as case 2 will work here.
As for implementation, note that the actual algorithm is exactly the same across all three cases: so you don’t actually need to perform any casework to implement it.
TIME COMPLEXITY
\mathcal{O}(N) or \mathcal{O}(26\cdot N) per testcase.
CODE:
Author's code (C++, O(26N))
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ios_base::sync_with_stdio(false);
cin.tie(NULL);
ll kitne_cases_hain;
kitne_cases_hain=1;
//freopen("input.txt","r",stdin);freopen("output.txt","w",stdout);
cin>>kitne_cases_hain;
while(kitne_cases_hain--){
ll n;
cin>>n;
string s;
cin>>s;
if((n%3)==1){
cout<<"YES\n";
continue;
}
ll flag=0;
ll l,r;
for(int i=0;i<26;i++){
l=0;r=0;
for(int j=0;j<n;j++){
if((s[j]-'a')==i && (j%3)==0){
l=j+1;
break;
}
}
for(int j=n-1;j>0;j--){
if((s[j]-'a')==i && ((n-1-j)%3)==0){
r=j+1;
break;
}
}
if(r>l && l!=0){
flag=1;
break;
}
}
if(flag){
cout<<"YES\n";
}else{
cout<<"NO\n";
}
}
return 0;
}
Editorialist's code (Python, O(N))
for _ in range(int(input())):
n = int(input())
s = input()
left, right = [n]*26, [-1]*26
for i in range(n):
ch = ord(s[i]) - ord('a')
if i%3 == 0: left[ch] = min(left[ch], i)
if (n-1-i)%3 == 0: right[ch] = i
print('Yes' if sum(left[i] <= right[i] for i in range(26)) > 0 else 'No')