【发布时间】:2011-09-02 05:25:17
【问题描述】:
我正在尝试解决来自 Cormem 的 Introduction to Algorithms 3rd edition(第 405 页)的动态编程问题,该问题提出以下问题:
回文是一个非空字符串 一些读起来相同的字母 前进和后退。示例 回文都是长度的字符串 1、
civic、racecar和aibohphobia(害怕回文)。给出一个有效的算法来寻找 最长的回文是 给定输入字符串的子序列。 例如,给定输入
character,你的算法应该 返回carac。
嗯,我可以通过两种方式解决它:
第一个解决方案:
字符串的最长回文子序列 (LPS) 就是其自身的 Longest Common Subsequence 及其反向。 (我在解决另一个要求序列的Longest Increasing Subsequence 的相关问题后构建了这个解决方案)。 由于它只是一个 LCS 变体,因此也需要 O(n²) 时间和 O(n²) 内存。
第二种解决方案:
第二种解决方案更详细一些,但也遵循通用 LCS 模板。它来自以下重复:
lps(s[i..j]) =
s[i] + lps(s[i+1]..[j-1]) + s[j], if s[i] == s[j];
max(lps(s[i+1..j]), lps(s[i..j-1])) otherwise
计算lps长度的伪代码如下:
compute-lps(s, n):
// palindromes with length 1
for i = 1 to n:
c[i, i] = 1
// palindromes with length up to 2
for i = 1 to n-1:
c[i, i+1] = (s[i] == s[i+1]) ? 2 : 1
// palindromes with length up to j+1
for j = 2 to n-1:
for i = 1 to n-i:
if s[i] == s[i+j]:
c[i, i+j] = 2 + c[i+1, i+j-1]
else:
c[i, i+j] = max( c[i+1, i+j] , c[i, i+j-1] )
如果我想有效地构造 lps,它仍然需要 O(n²) 时间和内存(因为我需要桌子上的所有单元格)。分析相关问题,例如 LIS,可以用 LCS 以外的方法解决,用更少的内存(LIS 可以用 O(n) 内存解决),我想知道是否可以用 O(n) 内存解决它,也是。
LIS 通过链接候选子序列来实现这一界限,但对于回文则更难,因为这里重要的不是子序列中的前一个元素,而是第一个元素。有谁知道是否可以这样做,或者以前的解决方案内存是最优的吗?
【问题讨论】:
-
给定输入 character,您的算法肯定会返回 ara,而不是 carac。
-
@Ergwun 据我了解,回文可以来自任何保留原始顺序的子集,而不仅仅是来自连接的子集。
-
@Ergwun: carac 确实在 c h arac ter.
-
啊,我不明白 subsequence 和 substring 之间的区别 - 谢谢。
-
子序列和子字符串的区别在于子字符串的元素必须出现在原始序列的连续索引处,而在子序列上,唯一的要求是保留它们的原始顺序。跨度>
标签: algorithm language-agnostic dynamic-programming palindrome