题目描述
给你两个字符串 word1
和 word2
,请你按下述方法构造一个字符串:
- 从
word1
中选出某个 非空 子序列subsequence1
。 - 从
word2
中选出某个 非空 子序列subsequence2
。 - 连接两个子序列
subsequence1 + subsequence2
,得到字符串。
返回可按上述方法构造的最长 回文串 的 长度。如果无法构造回文串,返回 0
。
字符串 s
的一个 子序列 是通过从 s
中删除一些(也可能不删除)字符而不更改其余字符的顺序生成的字符串。
回文串 是正着读和反着读结果一致的字符串。
样例
输入:word1 = "cacb", word2 = "cbba"
输出:5
解释:从 word1 中选出 "ab",从 word2 中选出 "cba",得到回文串 "abcba"。
输入:word1 = "ab", word2 = "ab"
输出:3
解释:从 word1 中选出 "ab",从 word2 中选出 "a",得到回文串 "aba"。
输入:word1 = "aa", word2 = "bb"
输出:0
解释:无法按题面所述方法构造回文串,所以返回 0。
限制
1 <= word1.length, word2.length <= 1000
word1
和word2
由小写英文字母组成。
算法
(区间动态规划) $O((n + m)^2)$
- 将两个字符串进行拼接,求拼接后字符串的最长回文子序列,但要保证答案对应原字符串中的子序列都非空。
- 设状态 $f(i, j)$ 表示拼接后字符串的区间
[i, j]
的最长回文子序列,且满足两个子序列都非空。状态 $g(i, j)$ 表示拼接后字符串的区间[i, j]
的最长回文子序列,但有一个子序列为空。 - 初始时,$f(i, i) = -\infty, g(i, i) = 1$。考虑边界,$f(i + 1, i) = -\infty, g(i + 1, i) = 0$。其余状态待定。
- 转移时:
- 如果 $a(i) == a(j)$
- 区间跨越了两个字符串,则可以保留 $a(i)$ 和 $a(j)$,可以从 $f(i + 1, j - 1) + 2$ 或者 $g(i + 1, j - 1) + 2$ 进行转移到 $f(i, j)$。
- 区间没有跨越两个字符串,则也可以保留 $a(i)$ 和 $a(j)$,只能从 $g(i + 1, j - 1) + 2$ 转移到 $g(i, j)$。
- 任何情况下,都可以从 $f(i + 1, j)$ 或者 $f(i, j - 1)$ 转移到 $f(i, j)$。从 $g(i + 1, j)$ 或者 $g(i, j - 1)$ 转移到 $g(i, j)$。这表示忽视 $a(i)$ 或者 $a(j)$,区间跨越性不变。
- 如果 $a(i) == a(j)$
- 最终答案为 $f(0, n - 1)$。如果答案为负无穷,则返回 0。
时间复杂度
- 状态数为 $O((n + m)^2)$,转移时间为常数,故总时间复杂度为 $O((n + m)^2)$。
空间复杂度
- 需要 $O((n + m)^2)$ 的额外空间存储状态。
C++ 代码
#define N 2010
class Solution {
private:
int f[N][N], g[N][N];
public:
int longestPalindrome(string word1, string word2) {
const int n = word1.size() + word2.size();
string a = word1 + word2;
for (int i = 0; i < n; i++) {
f[i][i] = INT_MIN;
g[i][i] = 1;
if (i < n - 1) {
f[i + 1][i] = INT_MIN;
g[i + 1][i] = 0;
}
}
for (int len = 2; len <= n; len++)
for (int i = 0; i < n - len + 1; i++) {
int j = i + len - 1;
f[i][j] = g[i][j] = INT_MIN;
if (a[i] == a[j]) {
if (i < word1.size() && j >= word1.size())
f[i][j] = max(f[i][j],
max(f[i + 1][j - 1], g[i + 1][j - 1]) + 2);
else
g[i][j] = max(g[i][j], g[i + 1][j - 1] + 2);
}
f[i][j] = max(f[i][j], max(f[i + 1][j], f[i][j - 1]));
g[i][j] = max(g[i][j], max(g[i + 1][j], g[i][j - 1]));
}
if (f[0][n - 1] < 0)
return 0;
return f[0][n - 1];
}
};
大佬这个题解是不是放错位置了?
麻蛋,lc 天天改题号真恶心