Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
You are given a string s.
Consider performing the following operation until s becomes empty:
'a' to 'z', remove the first occurrence of that character in s (if it exists).For example, let initially s = "aabcbbca". We do the following operations:
s = "aabcbbca". The resulting string is s = "abbca".s = "abbca". The resulting string is s = "ba".s = "ba". The resulting string is s = "".Return the value of the string s right before applying the last operation. In the example above, answer is "ba".
Example 1:
Input: s = "aabcbbca" Output: "ba" Explanation: Explained in the statement.
Example 2:
Input: s = "abcd" Output: "abcd" Explanation: We do the following operation: - Remove the underlined characters s = "abcd". The resulting string is s = "". The string just before the last operation is "abcd".
Constraints:
1 <= s.length <= 5 * 105s consists only of lowercase English letters.Problem summary: You are given a string s. Consider performing the following operation until s becomes empty: For every alphabet character from 'a' to 'z', remove the first occurrence of that character in s (if it exists). For example, let initially s = "aabcbbca". We do the following operations: Remove the underlined characters s = "aabcbbca". The resulting string is s = "abbca". Remove the underlined characters s = "abbca". The resulting string is s = "ba". Remove the underlined characters s = "ba". The resulting string is s = "". Return the value of the string s right before applying the last operation. In the example above, answer is "ba".
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
"aabcbbca"
"abcd"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3039: Apply Operations to Make String Empty
class Solution {
public String lastNonEmptyString(String s) {
int[] cnt = new int[26];
int[] last = new int[26];
int n = s.length();
int mx = 0;
for (int i = 0; i < n; ++i) {
int c = s.charAt(i) - 'a';
mx = Math.max(mx, ++cnt[c]);
last[c] = i;
}
StringBuilder ans = new StringBuilder();
for (int i = 0; i < n; ++i) {
int c = s.charAt(i) - 'a';
if (cnt[c] == mx && last[c] == i) {
ans.append(s.charAt(i));
}
}
return ans.toString();
}
}
// Accepted solution for LeetCode #3039: Apply Operations to Make String Empty
func lastNonEmptyString(s string) string {
cnt := [26]int{}
last := [26]int{}
mx := 0
for i, c := range s {
c -= 'a'
cnt[c]++
last[c] = i
mx = max(mx, cnt[c])
}
ans := []rune{}
for i, c := range s {
if cnt[c-'a'] == mx && last[c-'a'] == i {
ans = append(ans, c)
}
}
return string(ans)
}
# Accepted solution for LeetCode #3039: Apply Operations to Make String Empty
class Solution:
def lastNonEmptyString(self, s: str) -> str:
cnt = Counter(s)
mx = cnt.most_common(1)[0][1]
last = {c: i for i, c in enumerate(s)}
return "".join(c for i, c in enumerate(s) if cnt[c] == mx and last[c] == i)
// Accepted solution for LeetCode #3039: Apply Operations to Make String Empty
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3039: Apply Operations to Make String Empty
// class Solution {
// public String lastNonEmptyString(String s) {
// int[] cnt = new int[26];
// int[] last = new int[26];
// int n = s.length();
// int mx = 0;
// for (int i = 0; i < n; ++i) {
// int c = s.charAt(i) - 'a';
// mx = Math.max(mx, ++cnt[c]);
// last[c] = i;
// }
// StringBuilder ans = new StringBuilder();
// for (int i = 0; i < n; ++i) {
// int c = s.charAt(i) - 'a';
// if (cnt[c] == mx && last[c] == i) {
// ans.append(s.charAt(i));
// }
// }
// return ans.toString();
// }
// }
// Accepted solution for LeetCode #3039: Apply Operations to Make String Empty
function lastNonEmptyString(s: string): string {
const cnt: number[] = Array(26).fill(0);
const last: number[] = Array(26).fill(0);
const n = s.length;
let mx = 0;
for (let i = 0; i < n; ++i) {
const c = s.charCodeAt(i) - 97;
mx = Math.max(mx, ++cnt[c]);
last[c] = i;
}
const ans: string[] = [];
for (let i = 0; i < n; ++i) {
const c = s.charCodeAt(i) - 97;
if (cnt[c] === mx && last[c] === i) {
ans.push(String.fromCharCode(c + 97));
}
}
return ans.join('');
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.