算法导论—最长回文子串和子序列
2016-03-06 21:19
169 查看
华电北风吹
2016/3/5
回文串是指正序和逆序均相同的字符串。在回文子串的求解过程中,根据每个字符是否连续问题又可分为最长回文子串(绝对回文)和最长回文子序列(相对回文)。求解一个字符串中最长回文子串要求这个子串必须是连续的,而最长回文子序列则不要求这个子串是连续的,但是子串各个字符在原始串中的相对位置需要一致。例如对于字符串”character”,他的最长回文子串为”ara”,最长回文子序列则为”carac”。
一、最长回文子串
求解最长回文子串可以采用暴力破解(中心扩展)和动态规划(基于中心扩展,Manacher法)。
1、中心扩展
对于字符串的每个元素判断以他为中心的最长回文串长度。这里需要注意的是回文串分奇数串和偶数串,他们的代码可能会有稍微差异。中心扩展时间复杂度为O(n2)。
2、动态规划(Manacher)
动态规划算法基于中心扩展。在对字符串中的每个字符串进行中心扩展的时候,如果这个字符在对前面的字符中心扩展的时候已经访问过,说明可以利用这个字符在的那个中心字符对称位置的回文串长度来提高当前字符串初始值(对称位置的值和边界两者最小值)。动态规划时间复杂度为O(n)。
这个方法有个很巧妙的字符串预处理,在任意两个字符串之间插入一个分割字符,这样可以将原始串中的奇回文串和偶回文串全部一一对应到新串中,这样方便后续处理(否则,参考上文)。预处理后所有的回文串都变成奇数串了,并且都以分割字符结束。
参考博客:http://www.cnblogs.com/heyonggang/p/3386724.html
二、最长回文子序列
最长回文子序列的动态规划思路跟LCS特别相似。空间复杂度和时间复杂度都同为O(n2)。
图片展示如下:
![](https://img-blog.csdn.net/20160306211855412)
参考代码:
2016/3/5
回文串是指正序和逆序均相同的字符串。在回文子串的求解过程中,根据每个字符是否连续问题又可分为最长回文子串(绝对回文)和最长回文子序列(相对回文)。求解一个字符串中最长回文子串要求这个子串必须是连续的,而最长回文子序列则不要求这个子串是连续的,但是子串各个字符在原始串中的相对位置需要一致。例如对于字符串”character”,他的最长回文子串为”ara”,最长回文子序列则为”carac”。
一、最长回文子串
求解最长回文子串可以采用暴力破解(中心扩展)和动态规划(基于中心扩展,Manacher法)。
1、中心扩展
对于字符串的每个元素判断以他为中心的最长回文串长度。这里需要注意的是回文串分奇数串和偶数串,他们的代码可能会有稍微差异。中心扩展时间复杂度为O(n2)。
class Solution { public: string longestPalindrome(string s) { int n = s.length(); int maxlength1 = 0, position1 = 0; for (int i = 1; i < n; i++) { int j; for (j = 1; j <= n / 2; j++) { if ((i - j < 0) || (i + j >= n) || (s[i - j] != s[i + j])) break; } j--; if (j > maxlength1) { maxlength1 = j; position1 = i - maxlength1; } } int maxlength2 = 0, position2 = 0; for (int i = 1; i < n; i++) { if (s[i] == s[i - 1]) { int p = 2; while ((i - p >= 0) && (i + p - 1 < n) && (s[i - p] == s[i + p - 1])) p++; p--; if (p > maxlength2) { maxlength2 = p; position2 = i - p; } } } return maxlength1 >= maxlength2 ? (s.substr(position1, maxlength1 * 2 + 1)) : (s.substr(position2, maxlength2 * 2)); } };
2、动态规划(Manacher)
动态规划算法基于中心扩展。在对字符串中的每个字符串进行中心扩展的时候,如果这个字符在对前面的字符中心扩展的时候已经访问过,说明可以利用这个字符在的那个中心字符对称位置的回文串长度来提高当前字符串初始值(对称位置的值和边界两者最小值)。动态规划时间复杂度为O(n)。
这个方法有个很巧妙的字符串预处理,在任意两个字符串之间插入一个分割字符,这样可以将原始串中的奇回文串和偶回文串全部一一对应到新串中,这样方便后续处理(否则,参考上文)。预处理后所有的回文串都变成奇数串了,并且都以分割字符结束。
class Solution { public: string PreProcess(string str) { string s = ""; int length = str.length(); for (int i = 0; i<length; i++) { s.push_back('#'); s.push_back(str[i]); } s.push_back('#'); return s; } string longestPalindrome(string str) { string prestr = PreProcess(str); int rightBound = -1, pCenter = -1; int len = prestr.length(); int *p = new int[len](); for (int i = 0; i < len; i++) { if (rightBound >= i) p[i] = min(rightBound - i + 1, p[2 * pCenter - i]); else p[i] = 1; while (i - p[i] >= 0 && i + p[i] < len && prestr[i - p[i]] == prestr[i + p[i]]) p[i]++; if (i + p[i] - 1 > rightBound) { rightBound = p[i] + i - 1; pCenter = i; } } int maxIndex = 0; for (int i = 0; i<len; i++) { if (p[i]>p[maxIndex]) maxIndex = i; } int maxlength = p[maxIndex] - 1; delete[] p; return str.substr((maxIndex - maxlength) / 2, maxlength); } };
参考博客:http://www.cnblogs.com/heyonggang/p/3386724.html
二、最长回文子序列
最长回文子序列的动态规划思路跟LCS特别相似。空间复杂度和时间复杂度都同为O(n2)。
图片展示如下:
参考代码:
class Solution { public: int longestPalindrome(string str) { int n = str.length(); vector<vector<int>> count(n, vector<int>(n, 0)); for (int i = 0; i < n; i++) count[i][i] = 1; for (int i = n - 2; i >= 0; i--) { for (int j = i + 1; j < n; j++) { if (str[i] == str[j]) { if (j == i + 1) { count[i][j] = 2; } else { count[i][j] = count[i + 1][j - 1] + 2; } } else { count[i][j] = max(count[i + 1][j], count[i][j - 1]); } } } return count[0][n - 1]; } };
相关文章推荐
- freemarker数据类型
- JUnit快速入门
- 算法_10 : 图算法_5: 图的染色
- 最小二乘法直线拟合、圆拟合
- thinkphp自定义模板标签(二)
- 圣杯布局的实现过程
- thinkphp自定义模板标签(二)
- spring bean scope 单例与多例
- NBUT 1655:木块拼接【面积】
- 用户,用户组及权限管理
- freemarker模板解析过程
- iOS UI基础学习历程
- OnCreateContextMenuListener,MultiChoiceModeListener和OnLongClickListener的优先级
- 算法_10 : 图算法_3: 图的遍历、匹配
- Linux多线程编程实例解析
- CentOS 7.1编译安装Nginx1.9.0
- 【历届试题】大臣的旅费
- 傻瓜式理解之一致哈希(仅供初学者理解)
- 思考.视界
- freemarker常用的基本命令