Given two strings word1
and word2
, you are allowed to select any non-empty subsequence from word1
and any non-empty subsequence from word2
. Concatenate the chosen subsequences to form a new string. Your task is to return the maximum possible length of a palindrome that can be formed from such a concatenation.
word1
and word2
.At first glance, the problem seems to ask for all possible subsequences of both words, concatenate them, and check which concatenation yields the longest palindrome. However, this brute-force approach is infeasible for even moderately sized strings due to the exponential number of subsequences.
The key insight is to realize that the palindrome must be formed by combining subsequences from both word1
and word2
, and that the longest palindrome will be one where the left part comes from word1
and the right part comes from word2
.
We need to find a way to efficiently compute the maximum-length palindrome that can be constructed by concatenating subsequences from both words, ensuring that at least one character from each is used.
This naturally leads us to dynamic programming (DP), specifically to a DP that computes the longest palindromic subsequence in the concatenation of word1
and word2
, while ensuring both words contribute at least one character.
word1
and word2
to form a new string s = word1 + word2
. Let n1
be the length of word1
and n2
be the length of word2
.s
. Let dp[i][j]
represent the length of the LPS in the substring s[i...j]
.word1
and at least one from word2
. We achieve this by only considering palindromic subsequences that start in word1
(i.e., i < n1
) and end in word2
(i.e., j >= n1
), and where s[i] == s[j]
.(i, j)
where i
is in word1
and j
is in word2
, and s[i] == s[j]
, compute 2 + dp[i+1][j-1]
(since we're matching the two ends). Track the maximum value among all such pairs.We use a DP matrix because it allows us to efficiently compute the LPS for all substrings, and by only considering valid endpoints (one from each word), we ensure the constraint is satisfied.
Suppose word1 = "cacb"
and word2 = "cbba"
.
s = "cacbcbba"
n1 = 4
(indices 0-3), n2 = 4
(indices 4-7)s
to find the LPS.i
in 0..3
(word1) and j
in 4..7
(word2), if s[i] == s[j]
, we check 2 + dp[i+1][j-1]
.s[1] == s[6] == 'a'
, so we consider 2 + dp[2][5]
. Suppose dp[2][5] = 2
, so this gives a palindrome of length 4.O(2^{n1 + n2})
.s
takes O((n1+n2)^2)
time and space.(i, j)
with i < n1
and j >= n1
is O(n1 * n2)
, which is dominated by the DP table construction.O((n1+n2)^2)
for the DP table.
This problem challenges us to maximize the length of a palindrome formed by concatenating subsequences from two words, with the constraint that both must contribute. The optimal solution uses dynamic programming to efficiently compute the longest palindromic subsequence in the combined string, while ensuring the palindrome includes characters from both words. By carefully restricting our search to palindromes that start in word1
and end in word2
, we satisfy the constraints and achieve an elegant, efficient solution.
class Solution:
def longestPalindrome(self, word1: str, word2: str) -> int:
s = word1 + word2
n1 = len(word1)
n2 = len(word2)
n = n1 + n2
dp = [[0] * n for _ in range(n)]
for i in range(n-1, -1, -1):
dp[i][i] = 1
for j in range(i+1, n):
if s[i] == s[j]:
dp[i][j] = 2 + dp[i+1][j-1] if j - i > 1 else 2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
res = 0
for i in range(n1):
for j in range(n1, n):
if s[i] == s[j]:
res = max(res, dp[i][j])
return res
class Solution {
public:
int longestPalindrome(string word1, string word2) {
string s = word1 + word2;
int n1 = word1.size(), n2 = word2.size();
int n = n1 + n2;
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int i = n - 1; i >= 0; --i) {
dp[i][i] = 1;
for (int j = i + 1; j < n; ++j) {
if (s[i] == s[j]) {
dp[i][j] = (j - i > 1) ? 2 + dp[i + 1][j - 1] : 2;
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
int res = 0;
for (int i = 0; i < n1; ++i) {
for (int j = n1; j < n; ++j) {
if (s[i] == s[j]) {
res = max(res, dp[i][j]);
}
}
}
return res;
}
};
class Solution {
public int longestPalindrome(String word1, String word2) {
String s = word1 + word2;
int n1 = word1.length(), n2 = word2.length();
int n = n1 + n2;
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; --i) {
dp[i][i] = 1;
for (int j = i + 1; j < n; ++j) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = (j - i > 1) ? 2 + dp[i + 1][j - 1] : 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
int res = 0;
for (int i = 0; i < n1; ++i) {
for (int j = n1; j < n; ++j) {
if (s.charAt(i) == s.charAt(j)) {
res = Math.max(res, dp[i][j]);
}
}
}
return res;
}
}
var longestPalindrome = function(word1, word2) {
let s = word1 + word2;
let n1 = word1.length, n2 = word2.length;
let n = n1 + n2;
let dp = Array.from({length: n}, () => Array(n).fill(0));
for (let i = n - 1; i >= 0; --i) {
dp[i][i] = 1;
for (let j = i + 1; j < n; ++j) {
if (s[i] === s[j]) {
dp[i][j] = (j - i > 1) ? 2 + dp[i + 1][j - 1] : 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
let res = 0;
for (let i = 0; i < n1; ++i) {
for (let j = n1; j < n; ++j) {
if (s[i] === s[j]) {
res = Math.max(res, dp[i][j]);
}
}
}
return res;
};