最长回文子序列问题
问题描述
回文是正序与逆序相同的非空字符串。例如,所有长度为1的字符串、civic、racecar、aibohphobia(害怕回文之意)都是回文。
设计高效算法,求给定输入字符串的最长回文子序列。例如,给定输入 character,算法应该返回 carac。算法的运行时间是怎么样的?
题解
本题出自《算法导论》第 15 章的思考题15-3。这道题可以使用暴力罗列的方法做,那么总共会有 $2^n$ 种情况,算法的复杂度也为 $O(2^n)$ ,其中 $n$ 为输入字符串的长度。
为了减小算法的复杂度,可以使用动态规划的方法解决。
给定任意一个输入串 $X = {x_1, x_2, x_3, x_4… x_n}$,定义其子串 $X_{i,j} = { x_i, x_{i+1}, x_{i+2}, …, x_j}$。设一个函数 $f(X_{i,j})$,其定义如下:
$$f(X_{i,j})= 1 , X_{i,j}是回文序列 $$
$$f(X_{i,j})= 0 , X_{i,j}不是回文序列$$
对于任意一个子串 $X_{i,j}$,分以下几种情况讨论:
$ x_i = x_j $,则此时 $X_{i,j}$是否是回文子序列,与 $x_i$和 $x_j$无关,可以将其从 $ X_{ij} $中去除。如果 $X_{i+1, j-1} $是一个回文序列,则 $X_{i, j}$ 也是一个回文序列;如果 $X_{i+1, j-1} $不是一个回文序列,则 $X_{i, j}$也不是一个回文序列。例如,给定一个序列 $ 1w1 $,其中 $w$是一段字符串序列。由于这个序列左右两端是相同的,因此当 $w$是一个回文序列时, $1w1$才是一个回文序列;反之亦然。所以,我们可以给出如下式子:
$$ f(X_{i,j}) = f(X_{i+1, j - 1}), x_i = x_j$$
$ x_i \neq x_j$,则此时由于 $X_{i, j}$两端的字符不同,其当然不是回文序列。同上,我们有:
$$ f(X_{i,j}) = 0, x_i \neq x_j$$
除此之外,我们还知道,一个只含有单个字符的字符串是回文序列;一个只有两个字符的字符串,即 $X_{i, i+1}$,如果有 $x_i = x_{i+1} $,则其也是回文序列。
综上,我们给出 $f(X_{i, j})$的完整递归式:
$ f(X_{i, j}) = 1 , i = j $
$f(X_{i, j}) = 1 , x_i = x_j, j = i + 1 $
$ f(X_{i, j}) =f(X_{i+1, j-1}) ,x_i = x_j $
$f(X_{i, j}) = 0 , x_i \neq x_j $
我们不妨使用一个 $ n \times n $的矩阵来储存 $f(X_{i, j})$的值。我们给出如下伪代码:
1 |
|
使用Python的实现如下:
1 |
|
显然,该算法的复杂度为 $ O(n^2) $,远比暴力求解的 $O(2^n)$要好。