您的位置:首页 > 其它

(最长连续回文串---一个更容易想到的算法)Longest Palindromic Substring

2017-01-20 10:56 513 查看
关于这道题,我看了国内一些帖子,基本上都是从国外的帖子翻译的,大概有3种算法,动态规划(O(N2)),KMP匹配最长前缀(O(N2))和一个叫做Manacher(O(N))的算法。前两种算法可能比较容易想到,但是不管是复杂度还是编码难度,都不是一个优雅的选择,而第三种算法是一个非普适的算法,如果不事先知道,是几乎不可能在面试中想出来的,而且我认为在最坏情况下其复杂度也不是O(N)。

在这里我提供我的解法,效率不比Manacher差,复杂度为O(kn - k2),其中k为解的长度,在最坏情况下复杂度为O(N2),最好情况下为O(N)。

先贴上我的运行结果



还是比较理想的。

下面说下思路。

我的思路是

1、以整个串的中心为回文串的中心

2、①如果当前中心可以得到的最长回文串小于当前最大值,当前最大值就是答案,结束。②否则,从中心开始向两边迭代比较对称位置上的字符是否相等,记录当前最大值及开始和结束位置索引。③如果迭代结束也没有碰到不相等的字符,说明以当前中心为中心的串就是答案,结束。如果碰到不相等的字符,记录当前最大值和位置信息。

3、将中心位置分别向左、右移动半位(因为中心位置可能不是一个字符而在两个字符之间),跳到第2步。

解释一下,因为题目要求解最长子串,所以不必计算出所有中心的回文串,只要找到最大值就停止。但是如何判断找到最大值呢?考虑一个字符串,可能的最大回文子串就是它自身,以它自身的中点为中点,而这个中点每向左(右)移动半位,新中点可能的最大回文串长度为移动之前的长度减一。



图中黑线为整个字符串的中心,此时可能得到的最长子串的长度就是字符串的长度10。蓝线为向两边移动半位,可能得到的最长子串长度变为9(A到F、B到E),红线为再移动半位,可能得到的最长子串变为8(A到E、C到E)。

所以,我们从整个字符串的中点开始向两边扩展,如果在比较某个中点的对称位置时越过了边界,那么说明这就是最长子串,因为在这之后的所有中点可能的最长子串都不会超过这个值,所以最长子串只可能存在于之前已经比较过的位置,而如果之前有一个更长的子串,那么它一定在之前中点的比较中就已经越过边界并返回了。上图中,以D为中点发现所有边界内的对称位置都相等,那么答案就是7,若中点左移半位,能得到的最长子串也只有6了。而如果一直没有越界,由于在每次比较完毕后都会更新当前最大值,如果在比较某个中点时,发现当前可能的最大值小于当前已记录的最大值,那么当前已记录的最大值就是答案,因为在之后所有的比较中都不会有一个更长的串了。

贴上代码:

public String longestPalindrome(String s) {
char[] input = s.toCharArray();
int length = input.length;
int max = 0, left = 0, right = length - 1; // 当前最长回文串的长度和起始、结束位置
boolean odd; // 当前中点为一个字符或在两个字符之间
/**
* odd 初始化
*/
if (length % 2 == 0) {
odd = false;
} else {
odd = true;
}
for (int il = length / 2, ir; il >= 0;) {
int sl, sr; // 回文串中应该相等的两个字符的索引
ir = length - 1 - il; // 右边中点
int temp = 0; // 以i为中心的串的最大回文数
if (odd) {
sl = il - 1;
sr = il + 1;
temp = 1;   //如果是奇数,中点为一个字符,temp初始值为1
} else {
sl = il - 1;
sr = il;
--il; // 如果是偶数,将il左移一位
}
if ((sl + 1) * 2 <= max) {
return s.substring(left, right + 1); // 如果当前可能的最大值已不可能大于已知的最大值,返回
}
// 计算temp
while (true) {
if (input[sl] == input[sr]) {
temp += 2;
--sl;
++sr;
} else {
if(temp > max){ // 如果不相等,更新最大值和相应位置索引
max = temp;
left = sl + 1;
right = sr - 1;
}
break;
}
if (sl == -1 || sr == length) {
return s.substring(sl + 1, sr); // 如果sl或sr碰到边界,说明已找到答案,返回
}
}
if (odd) {
sl = ir - 1;
sr = ir + 1;
temp = 1;
} else {
sl = ir;
sr = ir + 1;
temp = 0;
++ir; // 如果是偶数,将ir右移一位
}
// 计算temp
while (true) {
if (input[sl] == input[sr]) {
temp += 2;
--sl;
++sr;
} else {
if(temp > max){ // 如果不相等,更新最大值和相应位置索引
max = temp;
left = sl + 1;
right = sr - 1;
}
break;
}
if (sl == -1 || sr == length) {
return s.substring(sl + 1, sr); // 如果sl或sr碰到边界,说明已找到答案,返回
}
}
// 反转odd
odd = !odd;
}
return "";
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: