LeetCode 1616 - Split Two Strings to Make Palindrome
This problem gives us two strings, a and b, both having the same length. We are allowed to choose a split index and divi
Difficulty: 🟡 Medium
Topics: Two Pointers, String
Solution
Problem Understanding
This problem gives us two strings, a and b, both having the same length. We are allowed to choose a split index and divide both strings at exactly the same position.
If we split:
aintoaprefix + asuffixbintobprefix + bsuffix
then we may form two candidate strings:
aprefix + bsuffixbprefix + asuffix
The task is to determine whether at least one of these combinations can become a palindrome.
A palindrome is a string that reads the same forward and backward.
The important detail is that the split can occur anywhere, including the boundaries of the string. That means one side of the split may be empty. For example:
- split at index
0 - split at index
n
are both valid.
The constraints are very important:
1 <= n <= 10^5- only lowercase English letters
- both strings have equal length
A length of 10^5 immediately tells us that quadratic solutions are too slow. Any approach that tries every split and reconstructs strings repeatedly will likely exceed time limits. We need something close to linear time.
Several edge cases are important:
- Single character strings are always valid because every single character string is a palindrome.
- One of the original strings may already be a palindrome.
- The valid palindrome may only appear after mixing the two strings.
- Mismatches may occur very early or very late in the strings.
- The split could happen at the beginning or end, meaning we may effectively choose only one original string.
The problem guarantees:
- Both strings have equal length.
- Strings contain only lowercase English letters.
- Length is at least
1, so empty input never occurs.
Approaches
Brute Force Approach
The brute force solution tries every possible split index.
For every split position i:
- Build
a[:i] + b[i:] - Build
b[:i] + a[i:] - Check whether either result is a palindrome
Palindrome checking itself costs O(n), and there are n + 1 possible split positions.
Therefore:
- Constructing and checking each candidate costs
O(n) - We do this
O(n)times
This leads to O(n^2) time complexity.
With n = 10^5, this becomes far too slow.
Key Insight for the Optimal Solution
A palindrome only fails when characters at mirrored positions differ.
Suppose we try to form:
a prefix + b suffix
We compare:
- left side from
a - right side from
b
using two pointers:
leftstarts at beginningrightstarts at end
As long as:
a[left] == b[right]
the palindrome condition still holds.
Eventually one of two things happens:
- We finish successfully
- We encounter the first mismatch
At the first mismatch, we no longer have freedom to keep mixing arbitrarily. The remaining middle section must already be a palindrome entirely inside one string.
So after the mismatch, we only need to check:
- Is
a[left:right+1]a palindrome? - OR is
b[left:right+1]a palindrome?
If either is true, then a valid split exists.
This observation reduces the problem from trying every split independently to a single linear scan.
We must test both directions:
a prefix + b suffixb prefix + a suffix
because either arrangement may succeed.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(n) | Try every split and construct candidate strings |
| Optimal | O(n) | O(1) | Two pointer scan with palindrome verification |
Algorithm Walkthrough
- Define a helper function
is_palindrome(s, left, right).
This function checks whether the substring s[left:right+1] is a palindrome using two pointers moving inward.
2. Define another helper function check(x, y).
This function tests whether combining:
x prefix + y suffix
can form a palindrome.
3. Inside check(x, y), initialize:
left = 0right = len(x) - 1
- Move the pointers inward while:
x[left] == y[right]
This means the outer characters still satisfy palindrome symmetry. 5. Stop at the first mismatch.
At this point, the remaining middle section must come entirely from one string. 6. Check whether either remaining substring is already a palindrome:
x[left:right+1]y[left:right+1]
If either is a palindrome, return True.
7. If the entire scan completes without mismatch, return True.
This means the mixed string already satisfies palindrome symmetry completely. 8. Run:
check(a, b)
and
check(b, a)
- If either succeeds, return
True. Otherwise returnFalse.
Why it works
The algorithm relies on the fact that palindrome validity depends only on mirrored character pairs.
While scanning from the outside inward, we are effectively deciding which parts come from which string. Once the first mismatch occurs, no further mixing can repair the mismatch pattern. The only possibility is that the remaining middle section already forms a palindrome entirely within one string.
Therefore checking the remaining substring inside either original string is sufficient and guarantees correctness.
Python Solution
class Solution:
def checkPalindromeFormation(self, a: str, b: str) -> bool:
def is_palindrome(s: str, left: int, right: int) -> bool:
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
def check(x: str, y: str) -> bool:
left = 0
right = len(x) - 1
while left < right and x[left] == y[right]:
left += 1
right -= 1
return (
is_palindrome(x, left, right)
or is_palindrome(y, left, right)
)
return check(a, b) or check(b, a)
The implementation starts with the is_palindrome helper, which performs a standard two pointer palindrome check on a substring. This avoids creating new strings and keeps memory usage constant.
The check helper performs the main logic. It compares the left side of one string against the right side of the other string. As long as characters match, the mixed palindrome remains valid.
When a mismatch appears, the algorithm checks whether the remaining middle portion is already a palindrome within either original string. If so, we can choose the split at exactly that location.
Finally, the solution checks both possible concatenation directions because either arrangement may produce the palindrome.
Go Solution
func checkPalindromeFormation(a string, b string) bool {
isPalindrome := func(s string, left int, right int) bool {
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
var check func(string, string) bool
check = func(x string, y string) bool {
left := 0
right := len(x) - 1
for left < right && x[left] == y[right] {
left++
right--
}
return isPalindrome(x, left, right) ||
isPalindrome(y, left, right)
}
return check(a, b) || check(b, a)
}
The Go implementation closely mirrors the Python version.
Strings in Go are indexed by bytes, which is safe here because the input contains only lowercase English letters. No Unicode handling is required.
The helper functions use closures for cleaner organization. Memory usage remains constant because no substring copies are created.
Worked Examples
Example 1
a = "x"
b = "y"
Since the length is 1, every single character string is already a palindrome.
Running check(a, b):
| left | right | condition |
|---|---|---|
| 0 | 0 | loop stops immediately |
Now check:
"x"palindrome → true"y"palindrome → true
Result:
true
Example 2
a = "xbdef"
b = "xecab"
Check a prefix + b suffix.
| left | right | a[left] | b[right] | match |
|---|---|---|---|---|
| 0 | 4 | x | b | no |
Mismatch occurs immediately.
Now check remaining substrings:
a = "xbdef"
b = "xecab"
Neither is a palindrome.
Now try the opposite direction.
Check b prefix + a suffix.
| left | right | b[left] | a[right] | match |
|---|---|---|---|---|
| 0 | 4 | x | f | no |
Again, immediate mismatch.
Neither remaining substring is a palindrome.
Result:
false
Example 3
a = "ulacfd"
b = "jizalu"
Check a prefix + b suffix.
| left | right | a[left] | b[right] | match |
|---|---|---|---|---|
| 0 | 5 | u | u | yes |
| 1 | 4 | l | l | yes |
| 2 | 3 | a | a | yes |
Pointers cross successfully.
This means:
"ula" + "alu" = "ulaalu"
which is a palindrome.
Result:
true
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each pointer moves inward at most once |
| Space | O(1) | Only a few variables are used |
The algorithm performs at most a constant number of linear scans across the strings. Each helper function processes characters using two pointers without allocating additional data structures. Therefore the runtime is linear and the extra memory usage is constant.
Test Cases
solution = Solution()
assert solution.checkPalindromeFormation("x", "y") == True
# Single character strings
assert solution.checkPalindromeFormation("xbdef", "xecab") == False
# No valid split exists
assert solution.checkPalindromeFormation("ulacfd", "jizalu") == True
# Mixed palindrome exists
assert solution.checkPalindromeFormation("abdef", "fecab") == True
# Valid palindrome after split
assert solution.checkPalindromeFormation("abcdef", "fedcba") == True
# Entire mixed string forms palindrome
assert solution.checkPalindromeFormation("aaaa", "bbbb") == True
# Repeated characters
assert solution.checkPalindromeFormation("abc", "def") == False
# Completely incompatible strings
assert solution.checkPalindromeFormation("race", "ecar") == True
# One direction succeeds immediately
assert solution.checkPalindromeFormation("aab", "baa") == True
# Palindrome formed near middle
assert solution.checkPalindromeFormation("abcc", "cbaa") == True
# Requires checking remaining substring
assert solution.checkPalindromeFormation("abcdxy", "yxdcba") == True
# Long symmetric crossing
assert solution.checkPalindromeFormation("abcxy", "yxdef") == False
# Mismatch with non-palindrome remainder
Test Summary
| Test | Why |
|---|---|
"x", "y" |
Smallest possible input |
"xbdef", "xecab" |
No valid palindrome formation |
"ulacfd", "jizalu" |
Standard successful mixed palindrome |
"abdef", "fecab" |
Valid split inside strings |
"abcdef", "fedcba" |
Entire crossing matches |
"aaaa", "bbbb" |
Repeated characters |
"abc", "def" |
Fully incompatible strings |
"race", "ecar" |
Opposite direction succeeds |
"aab", "baa" |
Odd length palindrome behavior |
"abcc", "cbaa" |
Remaining substring palindrome |
"abcdxy", "yxdcba" |
Longer successful matching |
"abcxy", "yxdef" |
Failure after mismatch |
Edge Cases
Single Character Strings
When both strings contain only one character, the answer is always true. Any single character string is inherently a palindrome. A naive implementation might incorrectly assume a split is necessary, but the problem allows empty prefixes and suffixes. The implementation handles this naturally because the two pointer loop immediately terminates and the substring palindrome check succeeds.
Mismatch at the First Character
Cases like:
a = "abc"
b = "xyz"
fail immediately at the outermost comparison. A buggy implementation might continue searching for another split unnecessarily. The optimal solution correctly recognizes that once the first mismatch occurs, only the remaining substring matters. Since neither remaining substring is a palindrome, the answer is correctly false.
One Original String Already Being a Palindrome
Suppose:
a = "racecar"
b = "abcdefg"
Even without mixing strings, the answer should be true because we may split at index 0 or n and effectively use only one string. The implementation handles this automatically because after mismatch detection, it checks whether either remaining substring is already a palindrome.