LeetCode题解:Longest Palindromic Substring
2015-08-28 10:50
507 查看
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
题意:给定一个字符串,找出最长的回文子串
解题思路:
Manacher算法其实是找出字符串最常回文子串穷举法的优化,所以理解这个算法之前可以先想想穷举法是怎么干的:
对于一个回文字符串,字符串中心向两边延展必然是相同的,所以我们可以从左向右遍历字符串,把每一个位置都看作中心,然后向两边延展,判断是否为回文字符串,记录下最长字符串。无疑,这样的解决办法时间复杂度是O(n^2),而且在查找过程还要区分字符串是奇数还是偶数,以保证选择正确的中心
那么我们能够怎样优化这个算法呢,对于任意一个长度为L的字符串,具有L+1个插入位置。假如这L+1个插入位置都被插入,字符串长度将变成2L+1,也就是说,字符串将始终为奇数,这样判断奇偶的问题就解决了。
所以Manacher算法的第一步,就是向字符串的每一个插入位置插入一个不会出现的字符,一般会选用#。
例如字符串“abba”,处理后变为“#a#b#b#a#”
同样的,我们需要以某个中心点向两边拓展,找回文字符串,所以设置一个数组记录每一个中心点所能得到最长回文字符串的长度,例如:
S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
P[i] - 1就是我们要求的最长长度
所以关键就在于,我们要怎么求P[i],而这也是Manacher算法作出优化的地方。
首先,我们用变量id记录中心点的位置。由于回文字符串关于中心对称的特性,那么长度为len的回文字符串关于中心对称的边界为:len/2 + id,我们用mx来表示边界。
所以下面就要求P[i]了,当mx-i大于p[i],意味着对称距离大于回文对称距离,而以id为中心,mx为边界的字符串为回文,所以p[i]=p[j]
当mx-i<=p[i],意味着回文对称距离大于实际对称距离,此时能判断相等的只有图中绿色框内的部分,框外的部分是无法判断的。所以框外的部分仍然需要按照普通方法匹配。最终可以得到代码如下:
代码:
题意:给定一个字符串,找出最长的回文子串
解题思路:
Manacher算法
Manacher是求字符串最长回文子串的O(n)时间解决算法,我在这里记录一下我的理解,便于大家理解这个算法,也便于我自己学习。Manacher算法其实是找出字符串最常回文子串穷举法的优化,所以理解这个算法之前可以先想想穷举法是怎么干的:
对于一个回文字符串,字符串中心向两边延展必然是相同的,所以我们可以从左向右遍历字符串,把每一个位置都看作中心,然后向两边延展,判断是否为回文字符串,记录下最长字符串。无疑,这样的解决办法时间复杂度是O(n^2),而且在查找过程还要区分字符串是奇数还是偶数,以保证选择正确的中心
那么我们能够怎样优化这个算法呢,对于任意一个长度为L的字符串,具有L+1个插入位置。假如这L+1个插入位置都被插入,字符串长度将变成2L+1,也就是说,字符串将始终为奇数,这样判断奇偶的问题就解决了。
所以Manacher算法的第一步,就是向字符串的每一个插入位置插入一个不会出现的字符,一般会选用#。
例如字符串“abba”,处理后变为“#a#b#b#a#”
同样的,我们需要以某个中心点向两边拓展,找回文字符串,所以设置一个数组记录每一个中心点所能得到最长回文字符串的长度,例如:
S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
P[i] - 1就是我们要求的最长长度
所以关键就在于,我们要怎么求P[i],而这也是Manacher算法作出优化的地方。
首先,我们用变量id记录中心点的位置。由于回文字符串关于中心对称的特性,那么长度为len的回文字符串关于中心对称的边界为:len/2 + id,我们用mx来表示边界。
所以下面就要求P[i]了,当mx-i大于p[i],意味着对称距离大于回文对称距离,而以id为中心,mx为边界的字符串为回文,所以p[i]=p[j]
当mx-i<=p[i],意味着回文对称距离大于实际对称距离,此时能判断相等的只有图中绿色框内的部分,框外的部分是无法判断的。所以框外的部分仍然需要按照普通方法匹配。最终可以得到代码如下:
代码:
public class Solution { public String longestPalindrome(String s) { String manacherStr = preProcess(s); int center = 0; int radius = 0; int[] manacherP = new int[manacherStr.length()]; for(int i = 1; i < manacherStr.length() - 1; i++){ int iMirror = 2 * center - i; if(iMirror >= 0 && iMirror < manacherStr.length()){ manacherP[i] = radius > i ? Math.min(radius - i, manacherP[iMirror]) : 0; } int left = i - 1 - manacherP[i]; int right = i + 1 + manacherP[i]; while(manacherStr.charAt(left) == manacherStr.charAt(right)){ left--; right++; manacherP[i]++; if(left < 0 || right >= manacherStr.length()){ break; } } if(i + manacherP[i] > radius){ center = i; radius = i + manacherP[i]; } } int maxLen = 0; int centerIndex = 0; for(int i = 1; i < manacherStr.length() - 1; i++){ if(manacherP[i] > maxLen){ maxLen = manacherP[i]; centerIndex = i; } } int start = (centerIndex - maxLen - 1) / 2; int end = start + maxLen; return s.substring(start, end); } private String preProcess(String s){ if(s.length() == 0){ return "^$"; } StringBuilder sb = new StringBuilder("^"); for(int i = 0; i < s.length(); i++){ sb.append("#" + s.charAt(i)); } sb.append("#$"); return sb.toString(); } }
相关文章推荐
- [工作需求]linux常用命令以及vim常用命令
- Scala中View Bounds代码实战及其在Spark中的应用源码解析之Scala学习笔记-35
- Java基础知识强化03:Java中的堆与栈
- JS实现仿苹果底部任务栏菜单效果代码
- SSH使用密钥登录并禁止口令登录实践
- java基础之 多线程
- 使用cocos2d-x时在ios上设置竖屏ZZ
- 桶排序时的gap以及桶的个数
- LeetCode题解:Longest Substring Without Repeating Characters
- QT QListWidget去掉滚动条
- LeetCode:Merge Two Sorted Lists
- 在CSS中设置浮动元素两端对齐的2种方法分享
- 浪潮存储合作伙伴大会IPF2015随笔
- 【linux】crontab的定时任务示例
- Android - 文件读写操作 总结
- vsftpd安装以及配置FTP虚拟用户实践
- Codeforces Round #316 (Div. 2)
- Xmanger远程桌面Ubuntu 12.04
- tableView设置cell高度及分组模式添加头与脚显示方法
- UVa 1451:Average(数形结合)