Facebook Pixel

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 return true.
  • 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 return true.
  • 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Initialize two pointers: Set i = 0 pointing to the start of the string and j = len(s) - 1 pointing to the end of the string.

  2. Initialize a counter: Set cnt = 0 to track the number of mismatched character pairs.

  3. 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.

  4. Count mismatches: For each iteration:

    • Compare s[i] and s[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)
  5. Move pointers inward: After each comparison, move i forward by 1 and j backward by 1 using i, j = i + 1, j - 1.

  6. Return the result: After checking all pairs, return cnt <= 2. This returns True if we found at most 2 mismatches (meaning we can fix them with at most 2 operations), and False 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 Evaluator

Example 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') with s[3] ('a')
  • They match! So cnt remains 0
  • Move pointers: i = 1, j = 2

Iteration 2:

  • Compare s[1] ('b') with s[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 is True

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 and j
  • 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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following problems can be solved with backtracking (select multiple)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More