2330. Valid Palindrome IV π
Problem Description
You are given a string s
consisting of only lowercase English letters, indexed starting from 0. You can perform operations on this string where each operation allows you to change any character to any other character.
The task is to determine if you can transform the string s
into a palindrome by performing exactly one or two operations. A palindrome is a string that reads the same forwards and backwards.
You need to return true
if it's possible to make s
a palindrome with exactly 1 or 2 character changes, and false
otherwise.
For example:
- If
s = "abca"
, you can change the second character 'b' to 'c' and the third character 'c' to 'b' to get "acba", which is a palindrome. This takes 2 operations, so returntrue
. - If
s = "aa"
, the string is already a palindrome, but you must perform at least 1 operation. You could change any character and then change it back, using 2 operations total, so returntrue
. - If the string requires more than 2 changes to become a palindrome, return
false
.
The solution uses two pointers starting from both ends of the string, moving towards the center while counting mismatched character pairs. Since each mismatch requires one operation to fix, if the count of mismatches is at most 2, the string can be made into a palindrome within the operation limit.
Intuition
To make a string a palindrome, we need characters at corresponding positions from the start and end to match. For a string to be a palindrome, s[0]
must equal s[n-1]
, s[1]
must equal s[n-2]
, and so on.
The key insight is that when we compare characters from both ends moving inward, each pair of mismatched characters represents exactly one required operation. Why? Because we can change either character in the mismatched pair to match the other one.
For instance, if s[i] != s[j]
where i
and j
are corresponding positions from start and end, we need exactly one operation to fix this mismatch - we can either change s[i]
to match s[j]
, or change s[j]
to match s[i]
.
Since we're allowed exactly 1 or 2 operations, we can tolerate at most 2 mismatched pairs. If we find 0 mismatches, the string is already a palindrome but we still need to perform operations (we could change any character and change it back). If we find 1 or 2 mismatches, we can fix them within our operation limit. If we find more than 2 mismatches, it's impossible to create a palindrome with just 1 or 2 operations.
This naturally leads to a two-pointer approach: start from both ends, move towards the center, count the mismatches, and check if the count is at most 2.
Learn more about Two Pointers patterns.
Solution Approach
The solution implements a two-pointer technique to efficiently check the number of mismatches in the string.
Implementation Steps:
-
Initialize two pointers: Set
i = 0
pointing to the start of the string andj = len(s) - 1
pointing to the end of the string. -
Initialize a counter: Set
cnt = 0
to track the number of mismatched character pairs. -
Compare characters from both ends: Use a while loop with condition
i < j
to ensure we only check each pair once and stop when the pointers meet or cross. -
Count mismatches: For each iteration:
- Compare
s[i]
ands[j]
- If they don't match (
s[i] != s[j]
), increment the counter by 1 - This is done concisely with
cnt += s[i] != s[j]
(adds 1 if True, 0 if False)
- Compare
-
Move pointers inward: After each comparison, move
i
forward by 1 andj
backward by 1 usingi, j = i + 1, j - 1
. -
Return the result: After checking all pairs, return
cnt <= 2
. This returnsTrue
if we found at most 2 mismatches (meaning we can fix them with at most 2 operations), andFalse
otherwise.
Time Complexity: O(n)
where n
is the length of the string, as we traverse the string once.
Space Complexity: O(1)
as we only use a constant amount of extra space for the pointers and counter.
The elegance of this solution lies in its simplicity - by recognizing that each mismatch corresponds to exactly one required operation, we reduce the problem to simply counting mismatches.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the algorithm with the string s = "abca"
.
Initial Setup:
- String:
s = "abca"
(length = 4) - Left pointer:
i = 0
(pointing to 'a') - Right pointer:
j = 3
(pointing to 'a') - Mismatch counter:
cnt = 0
Iteration 1:
- Compare
s[0]
('a') withs[3]
('a') - They match! So
cnt
remains 0 - Move pointers:
i = 1
,j = 2
Iteration 2:
- Compare
s[1]
('b') withs[2]
('c') - They don't match! Increment
cnt = 1
- Move pointers:
i = 2
,j = 1
Termination:
- Loop ends because
i >= j
(2 >= 1) - Final count:
cnt = 1
- Return
cnt <= 2
? β1 <= 2
isTrue
Result: The function returns True
, meaning we can make "abca" a palindrome with exactly 1 or 2 operations. Indeed, we can change either the 'b' to 'c' or the 'c' to 'b' to create the palindrome "acca" or "abba" with just 1 operation.
Let's also look at a case that returns False
. Consider s = "abcdef"
:
Initial Setup:
- String:
s = "abcdef"
(length = 6) i = 0
,j = 5
,cnt = 0
Iteration 1: Compare 'a' with 'f' β mismatch, cnt = 1
Iteration 2: Compare 'b' with 'e' β mismatch, cnt = 2
Iteration 3: Compare 'c' with 'd' β mismatch, cnt = 3
Result: cnt = 3
, and 3 <= 2
is False
. We need at least 3 operations to make this a palindrome, which exceeds our limit.
Solution Implementation
1class Solution:
2 def makePalindrome(self, s: str) -> bool:
3 """
4 Check if a string can be made into a palindrome by changing at most 2 characters.
5
6 Args:
7 s: Input string to check
8
9 Returns:
10 True if the string can be made into a palindrome with at most 2 character changes
11 """
12 # Initialize two pointers: left pointer at start, right pointer at end
13 left = 0
14 right = len(s) - 1
15
16 # Counter for the number of mismatched character pairs
17 mismatch_count = 0
18
19 # Compare characters from both ends moving towards the center
20 while left < right:
21 # If characters at current positions don't match, increment mismatch counter
22 if s[left] != s[right]:
23 mismatch_count += 1
24
25 # Move pointers towards the center
26 left += 1
27 right -= 1
28
29 # Return True if we need to change at most 2 characters to form a palindrome
30 # Each mismatch requires changing one character to match its pair
31 return mismatch_count <= 2
32
1class Solution {
2 /**
3 * Determines if a string can be made into a palindrome by changing at most 2 characters.
4 *
5 * @param s the input string to check
6 * @return true if the string can be made into a palindrome with at most 2 character changes, false otherwise
7 */
8 public boolean makePalindrome(String s) {
9 // Counter for the number of mismatched character pairs
10 int mismatchCount = 0;
11
12 // Two pointers: left starting from beginning, right starting from end
13 int left = 0;
14 int right = s.length() - 1;
15
16 // Compare characters from both ends moving towards the center
17 while (left < right) {
18 // If characters at symmetric positions don't match, increment the mismatch counter
19 if (s.charAt(left) != s.charAt(right)) {
20 mismatchCount++;
21 }
22
23 // Move pointers towards the center
24 left++;
25 right--;
26 }
27
28 // A string can be made into a palindrome with at most 2 changes
29 // if there are at most 2 mismatched pairs
30 return mismatchCount <= 2;
31 }
32}
33
1class Solution {
2public:
3 bool makePalindrome(string s) {
4 // Count the number of mismatched character pairs
5 int mismatchCount = 0;
6
7 // Use two pointers: left starting from beginning, right from end
8 int left = 0;
9 int right = s.size() - 1;
10
11 // Compare characters from both ends moving towards center
12 while (left < right) {
13 // If characters at current positions don't match, increment mismatch count
14 if (s[left] != s[right]) {
15 mismatchCount++;
16 }
17
18 // Move pointers towards center
19 left++;
20 right--;
21 }
22
23 // A string can be made palindrome with at most 2 changes
24 // (changing 2 characters can fix up to 2 mismatched pairs)
25 return mismatchCount <= 2;
26 }
27};
28
1/**
2 * Checks if a string can be made into a palindrome by changing at most 2 characters
3 * @param s - The input string to check
4 * @returns true if the string can be made into a palindrome with at most 2 character changes, false otherwise
5 */
6function makePalindrome(s: string): boolean {
7 // Counter for the number of mismatched character pairs
8 let mismatchCount: number = 0;
9
10 // Left pointer starting from the beginning of the string
11 let leftIndex: number = 0;
12
13 // Right pointer starting from the end of the string
14 let rightIndex: number = s.length - 1;
15
16 // Compare characters from both ends moving towards the center
17 while (leftIndex < rightIndex) {
18 // If characters at symmetric positions don't match, increment the mismatch counter
19 if (s[leftIndex] !== s[rightIndex]) {
20 mismatchCount++;
21 }
22
23 // Move pointers towards the center
24 leftIndex++;
25 rightIndex--;
26 }
27
28 // Return true if we need to change at most 2 characters to form a palindrome
29 return mismatchCount <= 2;
30}
31
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the string s
. The algorithm uses two pointers (i
and j
) that start from opposite ends of the string and move toward each other. The while loop continues as long as i < j
, which means each character is visited at most once. Since the pointers traverse approximately n/2
iterations, the total time complexity is linear with respect to the input size.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space regardless of the input size. The variables used are:
- Two integer pointers
i
andj
- One integer counter
cnt
These variables require a fixed amount of memory that doesn't scale with the input string length, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding the Operation Requirement
The Problem: A common misinterpretation is thinking that we need to perform exactly 1 or 2 operations, rather than at most 2 operations. This leads to incorrect logic where strings that are already palindromes (0 mismatches) or require only 1 change might be incorrectly rejected.
Example of Incorrect Thinking:
# WRONG: Checking for exactly 1 or 2 operations
def makePalindrome_wrong(s: str) -> bool:
# ... counting logic ...
return mismatch_count == 1 or mismatch_count == 2 # This would fail for already-palindromes
The Fix: The correct interpretation is that we can perform up to 2 operations. The solution should return True
for 0, 1, or 2 mismatches:
return mismatch_count <= 2 # Correct: allows 0, 1, or 2 operations
Pitfall 2: Double-Counting Middle Character in Odd-Length Strings
The Problem: When dealing with odd-length strings, some might worry about the middle character or try to handle it separately, leading to unnecessary complexity or errors.
Example of Overcomplication:
# UNNECESSARY: Special handling for middle character
def makePalindrome_overcomplex(s: str) -> bool:
n = len(s)
left, right = 0, n - 1
mismatch_count = 0
while left < right:
if s[left] != s[right]:
mismatch_count += 1
left += 1
right -= 1
# Unnecessary check for middle character
if n % 2 == 1:
# Some might think special logic is needed here
pass
return mismatch_count <= 2
Why It's Not Needed: The middle character in an odd-length palindrome doesn't need a pair to match with, so it never contributes to the mismatch count. The left < right
condition naturally handles this by stopping before examining the middle character.
Pitfall 3: Confusing Character Changes with Number of Operations
The Problem: Some might think that changing both characters in a mismatched pair requires 2 operations, leading to incorrect counting.
Incorrect Understanding:
- String: "abcd"
- Comparing 'a' with 'd' β mismatch
- Thinking: "I need to change 'a' to 'd' OR 'd' to 'a', but that's 2 characters affected"
The Correct Understanding: Each mismatch requires exactly ONE operation to fix:
- You only need to change one character from the pair to match the other
- For "abcd": change 'a' to 'd' (1 operation) OR change 'd' to 'a' (1 operation)
- Not both!
Pitfall 4: Off-by-One Errors with Pointer Movement
The Problem: Incorrect pointer initialization or movement can lead to missing comparisons or comparing the same position.
Common Mistakes:
# WRONG: Starting right pointer at wrong position
right = len(s) # Should be len(s) - 1
# WRONG: Using wrong comparison operator
while left <= right: # This would compare middle character with itself in odd-length strings
The Correct Approach:
left = 0
right = len(s) - 1
while left < right: # Ensures we stop when pointers meet or cross
Which of the following problems can be solved with backtracking (select multiple)
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!