Facebook Pixel

1616. Split Two Strings to Make Palindrome

Problem Description

You are given two strings a and b that have the same length. Your task is to determine if you can form a palindrome by splitting both strings at the same index and combining parts from each string.

Here's how the splitting works:

  • Choose any index (including 0 or the length of the strings) to split both strings at the same position
  • String a gets split into a_prefix and a_suffix where a = a_prefix + a_suffix
  • String b gets split into b_prefix and b_suffix where b = b_prefix + b_suffix
  • Either part (prefix or suffix) can be empty

After splitting, you need to check if either of these two combinations forms a palindrome:

  1. a_prefix + b_suffix
  2. b_prefix + a_suffix

The function should return true if it's possible to form a palindrome using any valid split, otherwise return false.

For example, if s = "abc", valid splits include:

  • "" + "abc" (split at index 0)
  • "a" + "bc" (split at index 1)
  • "ab" + "c" (split at index 2)
  • "abc" + "" (split at index 3)

The solution uses a two-pointer approach where pointers start from opposite ends of the two strings. One pointer i starts from the beginning of one string while pointer j starts from the end of the other string. The pointers move towards the center as long as the characters match. If they cross (meaning i >= j), a palindrome can be formed. If they stop before crossing due to a mismatch, the algorithm checks if the remaining middle portion from either string forms a palindrome by itself. The process is repeated by swapping the roles of strings a and b.

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

Intuition

The key insight is that for a string to be a palindrome, characters at symmetric positions from both ends must match. When we combine a_prefix + b_suffix to form a palindrome, the beginning of a_prefix must match with the end of b_suffix in reverse order.

Let's think about what happens when we try to form a palindrome from a_prefix + b_suffix:

  • The first character of a (at index 0) should equal the last character of b (at index n-1)
  • The second character of a (at index 1) should equal the second-to-last character of b (at index n-2)
  • This pattern continues...

We can check this condition using two pointers moving towards each other. Start with pointer i at the beginning of string a and pointer j at the end of string b. As long as a[i] == b[j], we know these characters will be in correct palindromic positions in the final string, so we can move both pointers inward (i++ and j--).

At some point, we'll encounter one of two scenarios:

  1. The pointers cross (i >= j): This means we've successfully matched all necessary characters from the outside-in. The remaining portion (if any) is already in the middle of our combined string and will naturally form a palindrome.

  2. The pointers stop due to a mismatch (a[i] != b[j]): Now we have a middle section that hasn't been validated. However, this middle section comes entirely from one string - either a[i...j] or b[i...j]. If either of these substrings is already a palindrome by itself, then our combined string will also be a palindrome.

Why does checking the middle substring from either string work? Because once we've verified the outer portions match correctly, we just need the middle part to be palindromic on its own. It doesn't matter which string this middle part comes from - we can choose our split point accordingly.

Since we have two possible combinations (a_prefix + b_suffix or b_prefix + a_suffix), we need to check both possibilities by running the same algorithm with the roles of a and b swapped.

Learn more about Two Pointers patterns.

Solution Approach

The solution implements a two-pointer approach with helper functions to check palindrome formation possibilities.

Main Function Structure: The main function checkPalindromeFormation calls a helper function check1 twice - once with (a, b) and once with (b, a). This covers both possible combinations: a_prefix + b_suffix and b_prefix + a_suffix.

Helper Function check1: This function implements the two-pointer validation:

  1. Initialize pointer i at index 0 (start of string a)
  2. Initialize pointer j at index len(b) - 1 (end of string b)
  3. While i < j and a[i] == b[j]:
    • Move both pointers toward center: i = i + 1 and j = j - 1
    • This validates that the outer characters will form palindromic pairs

Checking the Middle Section: After the pointers stop moving, we have three possible outcomes:

  • If i >= j: The pointers have crossed, meaning all necessary characters match. Return true.
  • Otherwise, check if the middle substring from either string is a palindrome:
    • Call check2(a, i, j) to verify if a[i...j] is a palindrome
    • Call check2(b, i, j) to verify if b[i...j] is a palindrome
    • If either returns true, the combined string can form a palindrome

Helper Function check2: This function checks if a substring is a palindrome:

  • Extract substring a[i : j + 1] (inclusive of both i and j)
  • Compare it with its reverse a[i : j + 1][::-1]
  • Return true if they match

Why This Works: The algorithm efficiently validates palindrome formation by:

  1. First matching characters from opposite ends of the two strings
  2. When a mismatch occurs, checking if the remaining middle portion (from either string) is already palindromic
  3. Trying both possible combinations by swapping the roles of strings a and b

The time complexity is O(n) where n is the length of the strings, as we traverse the strings at most twice and perform palindrome checks on substrings. The space complexity is O(n) for creating substring slices during palindrome validation.

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 walk through the solution with a = "abdef" and b = "fecab".

Step 1: Check if we can form a palindrome from a_prefix + b_suffix

Call check1(a="abdef", b="fecab"):

  • Initialize pointers: i = 0 (pointing to 'a' in string a), j = 4 (pointing to 'b' in string b)
  • Check if a[0] == b[4]: 'a' == 'b'? No, mismatch immediately!
  • Since pointers haven't crossed (i < j), check the middle sections:
    • check2(a, 0, 4): Is a[0:5] = "abdef" a palindrome? No
    • check2(b, 0, 4): Is b[0:5] = "fecab" a palindrome? No
  • This combination doesn't work

Step 2: Check if we can form a palindrome from b_prefix + a_suffix

Call check1(b="fecab", a="abdef"):

  • Initialize pointers: i = 0 (pointing to 'f' in string b), j = 4 (pointing to 'f' in string a)
  • Check if b[0] == a[4]: 'f' == 'f'? Yes, match!
  • Move pointers: i = 1, j = 3
  • Check if b[1] == a[3]: 'e' == 'e'? Yes, match!
  • Move pointers: i = 2, j = 2
  • Now i >= j (both are at index 2), pointers have crossed!
  • Return true

Verification: The algorithm found that splitting at index 2 works:

  • b_prefix = "fe" (first 2 characters of b)
  • a_suffix = "def" (last 3 characters of a)
  • Combined string: "fe" + "def" = "fedef"
  • Is "fedef" a palindrome? Yes! (reads the same forwards and backwards)

The algorithm correctly returns true because we can form the palindrome "fedef" by combining the prefix of b with the suffix of a.

Solution Implementation

1class Solution:
2    def checkPalindromeFormation(self, a: str, b: str) -> bool:
3        """
4        Check if we can form a palindrome by taking a prefix from one string
5        and a suffix from another string.
6      
7        Args:
8            a: First string
9            b: Second string
10          
11        Returns:
12            True if a palindrome can be formed, False otherwise
13        """
14      
15        def can_form_palindrome_with_split(string1: str, string2: str) -> bool:
16            """
17            Try to form a palindrome by taking prefix from string1 and suffix from string2.
18          
19            Args:
20                string1: String to take prefix from
21                string2: String to take suffix from
22              
23            Returns:
24                True if palindrome can be formed with this combination
25            """
26            left = 0
27            right = len(string2) - 1
28          
29            # Match characters from outside moving inward
30            # string1[left] should equal string2[right] for palindrome
31            while left < right and string1[left] == string2[right]:
32                left += 1
33                right -= 1
34          
35            # If pointers crossed, we already have a palindrome
36            # Otherwise, check if remaining middle part is palindrome in either string
37            return (left >= right or 
38                    is_palindrome(string1, left, right) or 
39                    is_palindrome(string2, left, right))
40      
41        def is_palindrome(string: str, left: int, right: int) -> bool:
42            """
43            Check if substring from index left to right (inclusive) is a palindrome.
44          
45            Args:
46                string: The string to check
47                left: Starting index
48                right: Ending index (inclusive)
49              
50            Returns:
51                True if the substring is a palindrome
52            """
53            substring = string[left:right + 1]
54            return substring == substring[::-1]
55      
56        # Try both combinations: (prefix_a + suffix_b) and (prefix_b + suffix_a)
57        return can_form_palindrome_with_split(a, b) or can_form_palindrome_with_split(b, a)
58
1class Solution {
2    /**
3     * Checks if a palindrome can be formed by concatenating a prefix of one string
4     * with a suffix of another string.
5     * 
6     * @param a First string
7     * @param b Second string
8     * @return true if a palindrome can be formed, false otherwise
9     */
10    public boolean checkPalindromeFormation(String a, String b) {
11        // Try both combinations: (prefix of a + suffix of b) and (prefix of b + suffix of a)
12        return canFormPalindrome(a, b) || canFormPalindrome(b, a);
13    }
14
15    /**
16     * Checks if a palindrome can be formed by taking a prefix from string 'prefixStr'
17     * and a suffix from string 'suffixStr'.
18     * 
19     * @param prefixStr String providing the prefix
20     * @param suffixStr String providing the suffix
21     * @return true if a palindrome can be formed, false otherwise
22     */
23    private boolean canFormPalindrome(String prefixStr, String suffixStr) {
24        int left = 0;
25        int right = suffixStr.length() - 1;
26      
27        // Match characters from prefix start with suffix end, moving inward
28        while (left < right && prefixStr.charAt(left) == suffixStr.charAt(right)) {
29            left++;
30            right--;
31        }
32      
33        // If pointers crossed, the entire combination forms a palindrome
34        // Otherwise, check if the remaining middle part is a palindrome in either string
35        return left >= right || 
36               isPalindrome(prefixStr, left, right) || 
37               isPalindrome(suffixStr, left, right);
38    }
39
40    /**
41     * Checks if the substring of 'str' from index 'left' to 'right' (inclusive)
42     * forms a palindrome.
43     * 
44     * @param str The string to check
45     * @param left Starting index
46     * @param right Ending index
47     * @return true if the substring is a palindrome, false otherwise
48     */
49    private boolean isPalindrome(String str, int left, int right) {
50        // Check if characters at corresponding positions match
51        while (left < right && str.charAt(left) == str.charAt(right)) {
52            left++;
53            right--;
54        }
55      
56        // If pointers crossed or met, it's a palindrome
57        return left >= right;
58    }
59}
60
1class Solution {
2public:
3    /**
4     * Check if we can form a palindrome by splitting strings a and b at the same index
5     * and concatenating a_prefix + b_suffix or b_prefix + a_suffix
6     * @param a First string
7     * @param b Second string
8     * @return true if palindrome formation is possible, false otherwise
9     */
10    bool checkPalindromeFormation(string a, string b) {
11        // Try both combinations: (a_prefix + b_suffix) and (b_prefix + a_suffix)
12        return canFormPalindrome(a, b) || canFormPalindrome(b, a);
13    }
14
15private:
16    /**
17     * Check if we can form a palindrome using prefix of first string and suffix of second string
18     * @param first String providing the prefix
19     * @param second String providing the suffix
20     * @return true if palindrome can be formed, false otherwise
21     */
22    bool canFormPalindrome(string& first, string& second) {
23        int left = 0;
24        int right = second.size() - 1;
25      
26        // Match characters from first[left...] with second[...right]
27        // Moving inward while they match
28        while (left < right && first[left] == second[right]) {
29            ++left;
30            --right;
31        }
32      
33        // If pointers crossed, the entire combination forms a palindrome
34        // Otherwise, check if the middle portion of either string is a palindrome
35        return left >= right || 
36               isPalindrome(first, left, right) || 
37               isPalindrome(second, left, right);
38    }
39
40    /**
41     * Check if substring of given string from index left to right is a palindrome
42     * @param str String to check
43     * @param left Starting index
44     * @param right Ending index
45     * @return true if substring is palindrome, false otherwise
46     */
47    bool isPalindrome(string& str, int left, int right) {
48        // Check if the substring str[left...right] is a palindrome
49        while (left <= right && str[left] == str[right]) {
50            ++left;
51            --right;
52        }
53      
54        // If pointers crossed, the substring is a palindrome
55        return left >= right;
56    }
57};
58
1/**
2 * Checks if a palindrome can be formed by concatenating a prefix of one string
3 * with a suffix of another string
4 * @param a - First input string
5 * @param b - Second input string
6 * @returns true if a palindrome can be formed, false otherwise
7 */
8function checkPalindromeFormation(a: string, b: string): boolean {
9    /**
10     * Checks if a palindrome can be formed by taking prefix from first string
11     * and suffix from second string
12     * @param firstString - String to take prefix from
13     * @param secondString - String to take suffix from
14     * @returns true if palindrome can be formed
15     */
16    const checkPrefixSuffixCombination = (firstString: string, secondString: string): boolean => {
17        let leftIndex: number = 0;
18        let rightIndex: number = secondString.length - 1;
19      
20        // Match characters from outside moving inward
21        // Compare prefix of firstString with suffix of secondString
22        while (leftIndex < rightIndex && firstString.charAt(leftIndex) === secondString.charAt(rightIndex)) {
23            leftIndex++;
24            rightIndex--;
25        }
26      
27        // If pointers crossed, we found a complete palindrome
28        // Otherwise, try to complete the palindrome using middle portion from either string
29        return leftIndex >= rightIndex || 
30               checkMiddlePalindrome(firstString, leftIndex, rightIndex) || 
31               checkMiddlePalindrome(secondString, leftIndex, rightIndex);
32    };
33
34    /**
35     * Checks if the substring between given indices forms a palindrome
36     * @param str - String to check
37     * @param startIndex - Starting index of substring
38     * @param endIndex - Ending index of substring
39     * @returns true if substring is a palindrome
40     */
41    const checkMiddlePalindrome = (str: string, startIndex: number, endIndex: number): boolean => {
42        // Check if the middle portion of the string is a palindrome
43        while (startIndex < endIndex && str.charAt(startIndex) === str.charAt(endIndex)) {
44            startIndex++;
45            endIndex--;
46        }
47      
48        // If pointers crossed or met, the middle portion is a palindrome
49        return startIndex >= endIndex;
50    };
51  
52    // Try both combinations: (prefix of a + suffix of b) OR (prefix of b + suffix of a)
53    return checkPrefixSuffixCombination(a, b) || checkPrefixSuffixCombination(b, a);
54}
55

Time and Space Complexity

The time complexity is O(n), where n is the length of string a or b.

Time Complexity Analysis:

  • The check1 function iterates through the strings from both ends using two pointers i and j. In the worst case, this loop runs n/2 times, which is O(n).
  • The check2 function creates a substring a[i:j+1] and its reverse a[i:j+1][::-1], then compares them. Creating the substring takes O(j-i+1) time, reversing takes O(j-i+1) time, and comparison takes O(j-i+1) time. In the worst case, j-i+1 could be O(n).
  • The check2 function is called at most twice within check1.
  • The main function calls check1 twice: once with (a, b) and once with (b, a).
  • Overall: O(n) + O(n) = O(n)

The space complexity is O(n), not O(1) as stated in the reference answer.

Space Complexity Analysis:

  • The check2 function creates a substring using slicing a[i:j+1], which creates a new string of length up to n in the worst case, requiring O(n) space.
  • The reverse operation [::-1] also creates a new string of the same length, requiring another O(n) space.
  • Therefore, the space complexity is O(n) due to the string slicing and reversal operations in check2.

Note: The reference answer states space complexity is O(1), but this appears to be incorrect given the string slicing operations that create new strings rather than operating in-place.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Boundary Handling When Checking Middle Substring

A common mistake is misunderstanding what portion of the string needs to be checked for palindrome after the two-pointer matching stops. Developers often incorrectly assume they need to check the entire remaining string or use wrong indices.

Incorrect Implementation:

def can_form_palindrome_with_split(string1: str, string2: str) -> bool:
    left = 0
    right = len(string2) - 1
  
    while left < right and string1[left] == string2[right]:
        left += 1
        right -= 1
  
    # WRONG: Checking from left to end of string
    return is_palindrome(string1, left, len(string1) - 1)

Why it's wrong: After matching outer characters, we only need to check the middle portion between indices left and right, not from left to the end of the string.

Correct Approach: Check the substring from index left to right (inclusive) in either string, as shown in the original solution.

2. Forgetting to Check Both Strings for Middle Palindrome

Another pitfall is only checking if the middle portion of one string is a palindrome, missing valid solutions where the middle portion of the other string forms a palindrome.

Incorrect Implementation:

def can_form_palindrome_with_split(string1: str, string2: str) -> bool:
    left = 0
    right = len(string2) - 1
  
    while left < right and string1[left] == string2[right]:
        left += 1
        right -= 1
  
    # WRONG: Only checking string1's middle portion
    return left >= right or is_palindrome(string1, left, right)

Why it's wrong: Consider a = "abcdef" and b = "fecbaa". When we take prefix from a and suffix from b, we get a[0:2] + b[2:6] = "ab" + "cbaa". The pointers stop at left=2, right=3 because a[2]='c' != b[3]='b'. However, b[2:4] = "cb" is not a palindrome, but a[2:4] = "cd" isn't either in this case. We need to check both strings' middle portions.

Correct Approach: Always check if the middle substring is a palindrome in EITHER string1[left:right+1] OR string2[left:right+1].

3. Not Trying Both Combination Orders

A critical mistake is only checking one combination (prefix from a + suffix from b) and forgetting to check the other combination (prefix from b + suffix from a).

Incorrect Implementation:

def checkPalindromeFormation(self, a: str, b: str) -> bool:
    # WRONG: Only checking one combination
    return can_form_palindrome_with_split(a, b)

Why it's wrong: Consider a = "x" and b = "y". Taking prefix from a and suffix from b gives us "x", which is a palindrome. But if we only checked prefix from b and suffix from a, we'd get "y", also a palindrome. However, for more complex strings, one combination might work while the other doesn't.

Correct Approach: Always check both can_form_palindrome_with_split(a, b) OR can_form_palindrome_with_split(b, a) to cover all valid palindrome formations.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?


Recommended Readings

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

Load More