最长回文子串(动态规划和递归)
2016-04-04 10:24
197 查看
给一个字符串,找出它的最长的回文子序列的长度。例如,如果给定的序列是“BBABCBCAB”,则输出应该是7,“BABCBAB”是在它的最长回文子序列。 “BBBBB”和“BBCBB”也都是该字符串的回文子序列,但不是最长的。注意和最长回文子串的区别(参考:最长回文串)!这里说的子序列,类似最长公共子序列LCS( Longest
Common Subsequence)问题,可以是不连续的。这就是LPS(Longest Palindromic Subsequence)问题。
最直接的解决方法是:生成给定字符串的所有子序列,并找出最长的回文序列,这个方法的复杂度是指数级的。下面来分析怎么用动态规划解决。
假设 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)为去掉最后一个元素。
有了上面的公式,可以很容易的写出下面的递归程序:
Output: The lnegth of the LPS is 5 (即为: amama)
画出上面程序的递归树(部分),已一个长度为6 的字符串为例:
可见有许多重复的计算,例如L(1,4)。该问题符合动态规划的两个主要性质: [b][b]重叠子问题[/b][/b] 和 [b][b][b]最优子结构 [/b][/b][/b]。
下面通过动态规划的方法解决,通过自下而上的方式打表,存储子问题的最优解。
该算法的时间复杂度为O(n^2)。其实这个问题和 最长公共子序列 问题有些相似之处,我们可以对LCS算法做些修改,来解决此问题:
1) 对给定的字符串逆序 存储在另一个数组 rev[] 中
2) 再求这两个 字符串的 LCS的长度
时间复杂度也为 O(n^2)。
参考:http://www.geeksforgeeks.org/dynamic-programming-set-12-longest-palindromic-subsequence/
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( |
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 | } |
重叠子问题
画出上面程序的递归树(部分),已一个长度为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) |
下面通过动态规划的方法解决,通过自下而上的方式打表,存储子问题的最优解。
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 | } |
1) 对给定的字符串逆序 存储在另一个数组 rev[] 中
2) 再求这两个 字符串的 LCS的长度
时间复杂度也为 O(n^2)。
参考:http://www.geeksforgeeks.org/dynamic-programming-set-12-longest-palindromic-subsequence/
相关文章推荐
- Execute & ExecuteGlobal
- 策略模式使用场景
- 设计模式-builder
- 从paxos到zookeeper读书笔记
- Java路径操作具体解释
- 第六周心得
- Android课程---日历选择器和时间选择器
- xampp的安装和配置
- 观察者模式(这个用的太多了)
- 【bzoj1954】【The xor-longest Path】【trie树】
- R3多线程
- C#冒泡排序法及优化
- java正则表达式
- 导航栏透明度
- android studio 导入一个开源库文件汇总
- Java泛型
- Application.ProcessMessages
- LeetCode 185. Department Top Three Salaries
- yah3c在ubuntu下面的联网之路
- Powerful Incantation(HDU 4150)