您的位置:首页 > 其它

个人记录-LeetCode 5.Longest Palindromic Substring

2016-09-04 20:22 357 查看
问题

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.

问题要求找到一个字符串中最长的回文。

常规解法:

假设字符串长度为N,

因为需要找出最长的回文字符串,因此可以先判断长度为N的字符串是否回文;

如果长度为N的字符串不是回文,就依次判断长度为N-1的字符串是否回文;

依次类推,知道找到第一个回文字符串。

由于长度是依次递减的,因此第一个找到的回文字符串,肯定是最长的。

对应代码如下:

public class Solution {
public String longestPalindrome(String s) {
String result = null;

if (s != null) {
int len = s.length();
//i表示每次的选择的字符串长度,依次递减
for (int i = len; i >= 1; --i) {
//字符串首地址从0开始,依次增加
//取出长度为len的字符串
for (int j = 0; j <= len - i; ++j) {
//判断字符串是否回文
if (isPalindrome(s.substring(j, j + i))) {
return s.substring(j, j+i);
}
}
}
}

return result;
}

//判断字符串是否回文,其实就是判断字符串是否对称相等
private boolean isPalindrome(String s) {
boolean result = true;

int len = s.length();
for (int i = 0; i < len/2; ++i) {
if (s.charAt(i) != s.charAt(len - 1 - i)) {
result = false;
break;
}
}

return result;
}
}


这种做法是可行的,但显然算法复杂度达到了O(N3),不符合LeetCode的逼格。

进阶一:

前面方法存在的一个显著问题是,由于后一次比较无法利用前一次比较的信息,导致存在大量重复性的比较。

实际上,一个回文字符串以中心对称的子字符串也是回文的,因此我们应该先寻找短的回文串,并进行记录。下一次比较时,只需要比较两端增加的位置是否相等。

对应代码如下:

public class Solution {
public String longestPalindrome(String s) {
String result = null;

if (s != null) {
int len = s.length();
if(len <= 1) {
return s;
}

//compareCache[i][j]表示i~j位置的字符串是回文的,i为左下标,j为右下标
boolean[][] compareCache = new boolean[len][len];
compareCache[0][0] = true;
for (int i = 1; i < len; ++i) {
//一个长度初始化为true
compareCache[i][i] = true;
//右下标应该比左下标大,compareCache[i][i-1]无实际物理意义,仅为了便于处理长度为2的子串
compareCache[i][i-1] = true;
}

int leftIndex = 0;
int rightIndex = 0;

//subLen用于定义当前字符串长度
for (int subLen = 2; subLen <= len; ++subLen) {
//i用于定义当前字符串起始位置
for (int i = 0; i <= len - subLen; ++i) {
//当前字符串的子字符串为回文,并且两边新增字符相等时,该字符串也是回文的
//当长度为2时,不需要历史信息,因此上面对应compareCache初始化为true
if (s.charAt(i) == s.charAt(i + subLen - 1) && compareCache[i + 1][i + subLen -2]) {
compareCache[i][i + subLen - 1] = true;

//历史长度小于当前长度,进行更新
if (rightIndex - leftIndex + 1 < subLen) {
leftIndex = i;
rightIndex = i + subLen -1;
}
}
}
}

result = s.substring(leftIndex, rightIndex + 1);
}

return result;
}
}


由于增加了历史信息的对比,整个算法的复杂度达到了O(N2)。

进阶二

上面两种方法,均是通过不断试探起点,试探长度来探索出最长回文串。第二种方法只是在判断回文串的方式上,优于第一种方法,带来的效率上的提升,但思路的原点是一致的。

现在我们换一个思路,将判断回文串和试探长度的动作结合起来。

public class Solution {
public String longestPalindrome(String s) {
String result = null;

if (s != null) {
int len = s.length();
if(len <= 1) {
return s;
}

//startWithMaxLen中分别存储回文字符串的起始地址和长度
int[] startWithMaxLen = new int[] {0, 1};

for (int i = 1; i <= len-1; ++i) {
////寻找以i为中心的奇数长度的回文
updateStartAndMaxLen(s, len, i - 1, i + 1, startWithMaxLen);
//寻找以i-1,i为中点偶数长度的回文
updateStartAndMaxLen(s, len, i - 1, i, startWithMaxLen);
}

result = s.substring(startWithMaxLen[0], startWithMaxLen[0] + startWithMaxLen[1]);
}

return result;
}

private void updateStartAndMaxLen(String s, int len, int leftIndex,int rightIndex, int[] startWithMaxLen) {
while (leftIndex >= 0 && rightIndex <= len - 1 && s.charAt(leftIndex) == s.charAt(rightIndex)) {
leftIndex--;
rightIndex++;
}

leftIndex++;
rightIndex--;

int curLen = rightIndex - leftIndex + 1;
//更新数组
if (curLen > startWithMaxLen[1]) {
startWithMaxLen[0] = leftIndex;
startWithMaxLen[1] = curLen;
}
}
}


整个算法的复杂度也是O(N2),但对内存的占用更小。

进阶四 Manacher算法

关于这个算法的原理,在CSDN上看到了一个解释的最好的博客,自己就不再赘述了:

http://blog.csdn.net/dyx404514/article/details/42061017
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  LeetCode