Given a string s
, you are allowed to repeatedly delete characters from both ends of the string as long as the characters at both ends are equal (i.e., s[0] == s[n-1]
). In each operation, you remove all consecutive characters from the start and all consecutive characters from the end that are equal to each other. You must stop once the characters at the two ends are different or the string becomes empty.
Your task is to return the minimum length of the string that remains after performing this operation as many times as possible.
Constraints:
1 <= s.length <= 105
s
consists only of lowercase English letters.Let's break down the problem by thinking about how we can remove characters from both ends. At each step, if the first and last characters are equal, we can remove all consecutive occurrences of that character from both the start and the end. We repeat this process until the two ends have different characters or there is nothing left.
The brute-force approach would be to actually create new strings after every operation, but that would be inefficient for large strings. Instead, we can use two pointers: one starting from the left and one from the right, and move them inward as long as the current characters are equal.
The key insight is that we don't need to actually remove characters from the string; we just need to keep track of the current "active" substring using indices.
The optimal solution uses a two-pointer technique to efficiently process the string:
left = 0
and right = len(s) - 1
.left < right
and s[left] == s[right]
:
ch = s[left]
(the current character at both ends).left
forward as long as s[left] == ch
.right
backward as long as s[right] == ch
.left
to right
(inclusive).right - left + 1
if left <= right
, otherwise 0.This approach is efficient because each character is only visited at most once from each end, and we avoid creating new strings.
Example: s = "aabccabba"
left = 0
, right = 8
, s[left] = 'a'
, s[right] = 'a'
.left
rightward past all 'a's:
left = 0
('a'), left = 1
('a'), left = 2
('b')right
leftward past all 'a's:
right = 8
('a'), right = 7
('b')left = 2
('b'), right = 7
('b'). Both ends are 'b'. Move left
rightward:
left = 2
('b'), left = 3
('c')right
leftward:
right = 7
('b'), right = 6
('a')left = 3
('c'), right = 6
('a'). Ends are different, so we stop.left = 3
to right = 6
: "ccab". Length = 4.Brute-force approach: If we repeatedly create new substrings, each operation could take O(n) time, leading to O(n^2) in the worst case.
Optimized two-pointer approach: Each character is visited at most once by either pointer, so the total time complexity is O(n), where n is the length of the string. Space complexity is O(1) since we only use pointers and do not allocate extra space proportional to the input size.
The problem is efficiently solved using a two-pointer approach, where we move inward from both ends as long as the characters match, skipping all consecutive matching characters at each end. This avoids unnecessary string copying and ensures linear time performance. The key insight is to work with indices rather than modifying the string, making the solution both simple and efficient.
class Solution:
def minimumLength(self, s: str) -> int:
left, right = 0, len(s) - 1
while left < right and s[left] == s[right]:
ch = s[left]
while left <= right and s[left] == ch:
left += 1
while right >= left and s[right] == ch:
right -= 1
return right - left + 1 if left <= right else 0
class Solution {
public:
int minimumLength(string s) {
int left = 0, right = s.size() - 1;
while (left < right && s[left] == s[right]) {
char ch = s[left];
while (left <= right && s[left] == ch) ++left;
while (right >= left && s[right] == ch) --right;
}
return left <= right ? right - left + 1 : 0;
}
};
class Solution {
public int minimumLength(String s) {
int left = 0, right = s.length() - 1;
while (left < right && s.charAt(left) == s.charAt(right)) {
char ch = s.charAt(left);
while (left <= right && s.charAt(left) == ch) left++;
while (right >= left && s.charAt(right) == ch) right--;
}
return left <= right ? right - left + 1 : 0;
}
}
var minimumLength = function(s) {
let left = 0, right = s.length - 1;
while (left < right && s[left] === s[right]) {
let ch = s[left];
while (left <= right && s[left] === ch) left++;
while (right >= left && s[right] === ch) right--;
}
return left <= right ? right - left + 1 : 0;
};