您的位置:首页 > 其它

最长回文子串(动态规划和递归)

2016-04-04 10:24 197 查看
给一个字符串,找出它的最长的回文子序列的长度。例如,如果给定的序列是“BBABCBCAB”,则输出应该是7,“BABCBAB”是在它的最长回文子序列。 “BBBBB”和“BBCBB”也都是该字符串的回文子序列,但不是最长的。注意和最长回文子串的区别(参考:最长回文串)!这里说的子序列,类似最长公共子序列LCS( Longest
Common Subsequence)问题,可以是不连续的。这就是LPS(Longest Palindromic Subsequence)问题。

最直接的解决方法是:生成给定字符串的所有子序列,并找出最长的回文序列,这个方法的复杂度是指数级的。下面来分析怎么用动态规划解决。


1)最优子结构

假设 X[0 ... n-1] 是给定的序列,长度为n. 让 L(0,n-1) 表示 序列 X[0 ... n-1] 的最长回文子序列的长度。

1. 如果X的最后一个元素和第一个元素是相同的,这时:L(0, n-1) = L(1, n-2) + 2 , 还以 “BBABCBCAB” 为例,第一个和最后一个相同,因此 L(1,n-2) 就表示蓝色的部分。

2. 如果不相同:L(0, n-1) = MAX ( L(1, n-1) , L(0, n-2) )。 以”BABCBCA” 为例,L(1,n-1)即为去掉第一个元素的子序列,L(0, n-2)为去掉最后一个元素。

有了上面的公式,可以很容易的写出下面的递归程序:

01
#include<stdio.h>
02
#include<string.h>
03
int
lps(
char
*seq,
int
i,
int
j)
04
{
05
//一个元素即为1
06
if
(i
== j)
07
  
return
1;
08
if
(i
> j)
return
0;
//因为只计算序列
seq[i ... j]
09
10
//
如果首尾相同
11
if
(seq[i]
== seq[j])
12
return
lps
(seq, i+1, j-1) + 2;
13
14
//
首尾不同
15
return
max(
lps(seq, i,j-1), lps(seq, i+1, j) );
16
}
17
18
/*
测试 */
19
int
main()
20
{
21
 
char
seq[]
=
"acmerandacm"
;
22
 
int
n
=
strlen
(seq);
23
 
printf
(
"The
lnegth of the LPS is %d"
,
lps(seq, 0, n-1));
24
 
getchar
();
25
 
return
0;
26
}
Output: The lnegth of the LPS is 5 (即为: amama)


重叠子问题

画出上面程序的递归树(部分),已一个长度为6 的字符串为例:

1
 
L(0,
5)
2
  
/
\
3
 
/
\ 
4
L(1,5)
L(0,4)
5
  
/
\/ \
6
 
/
\/\
7
L(2,5)
L(1,4)  L(1,4)  L(0,3)
可见有许多重复的计算,例如L(1,4)。该问题符合动态规划的两个主要性质: [b][b]重叠子问题[/b][/b] 和 [b][b][b]最优子结构 [/b][/b][/b]。

下面通过动态规划的方法解决,通过自下而上的方式打表,存储子问题的最优解。

01
int
lpsDp(
char
*
str,
int
n){
02
 
int
dp

,
tmp;
03
 
memset
(dp,0,
sizeof
(dp));
04
 
for
(
int
i=0;
i<n; i++) dp[i][i] = 1;
05
 
//
i 表示 当前长度为 i+1的 子序列
06
 
for
(
int
i=1;
i<n; i++){
07
  
tmp
= 0;
08
  
//考虑所有连续的长度为i+1的子串.
该串为 str[j, j+i]
09
  
for
(
int
j=0;
j+i<n; j++){
10
//如果首尾相同
11
if
(str[j]
== str[j+i]){
12
 
tmp
= dp[j+1][j+i-1] + 2;
13
}
else
{
14
 
tmp
= max(dp[j+1][j+i],dp[j][j+i-1]);
15
}
16
dp[j][j+i]
= tmp;
17
  
}
18
 
}
19
 
//返回串
str[0][n-1] 的结果
20
 
return
dp[0][n-1];
21
}
该算法的时间复杂度为O(n^2)。其实这个问题和 最长公共子序列 问题有些相似之处,我们可以对LCS算法做些修改,来解决此问题:

1) 对给定的字符串逆序 存储在另一个数组 rev[] 中

2) 再求这两个 字符串的 LCS的长度

时间复杂度也为 O(n^2)。

参考:http://www.geeksforgeeks.org/dynamic-programming-set-12-longest-palindromic-subsequence/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: