「Leetcode」Longest Palindromic Substring——最长回文子串
问题描述
Given a string S ,find the longest palindromic substringin S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
什么是回文串
这种对称的,从前往后拼写和从后往前拼写一样的字符,分为奇数长度和偶数长度
“abba” “abcba” “abcddcba”
解决方案
1.暴力枚举
以每一个元素作为中间元素,同时从左右出发,复杂度为O(\(n^2\)),这里要考虑到奇数类型和偶数类型的回文串~
讲真,这种太慢了,AC后只打败了3%的人!
public class Solution {
/**
* @param s: input string
* @return: the longest palindromic substring
*/
public String longestPalindrome(String s) {
// write your code here
char[] S = s.toCharArray();
int sLength = S.length;
int maxLength = 1;
String resultSbuString = "";
for(int i=0;i<sLength;i++){
int l = -1;
int r = Integer.MAX_VALUE;
//偶数型
if(i + 1< sLength && S[i] == S[i+1]) {
int tempLength = 0;
String tempSubStr = "";
l = i - 1;
r = i + 2;
tempLength = 2;
tempSubStr = s.substring(i,i+2);
while(l >= 0 && r < sLength && S[l] == S[r]){
tempLength ++;
tempSubStr = s.substring(l,r+1 );
l--;
r++;
}
if(resultSbuString.length() < tempSubStr.length()){
maxLength = tempLength;
resultSbuString = tempSubStr;
}
}
//奇数型
if(i < sLength ){
int tempLength = 0;
String tempSubStr = "";
l = i-1;
r = i+1;
tempLength = 1;
tempSubStr = s.substring(i,i+1);
while(l >= 0 && r < sLength && S[l] == S[r]){
tempLength ++;
tempSubStr = s.substring(l,r+1 );
l--;
r++;
}
if(resultSbuString.length() < tempSubStr.length()){
maxLength = tempLength;
resultSbuString = tempSubStr;
}
}
}
return resultSbuString;
}
}
2.记忆化搜索
我们使用 f[i][j] = “aba” 表示字符串S中下标从 i ~ j 的最长回文子串是 “aba”
- 每次获取 f[i][j]的时候,先从二维数组中的记录中获取,如果没有那么再递归从他的子串中获取
- 如果 i == j ,说明这时候求的是一个字符的最长回文子串,就是这个 S[i] 字符自身
- 如果 i != j 的情况下,S[i] == S[j],并且 f[i+1][j-1] == S[i+1][j-1] ,那么 f[i][j] = S[i][j],
- 如果 i != j ,而且S[i] != S[j],那么 f[i][j] 的来源有三个,f[i+1][j-1] , f[i+1][j], f[i][j-1], 取最大的即可
本质就是递归,是在递归的时候记录处理过的数据,这种方法的复杂度为O(\(n^2\)),AC后依然是只超过了3%的人,这就很难受。。。
public class Solution {
/**
* @param s: input string
* @return: the longest palindromic substring
*/
public String longestPalindrome(String s) {
// write your code here
char[] S = s.toCharArray();
int sLength = S.length;
String[][] f = new String[sLength ][sLength ];
return generateLongestPalindromeSubString(f,0,sLength-1, s, S);
}
private String generateLongestPalindromeSubString(String[][] f,int i,int j,String s,char[] S){
if(i<0 || j>=s.length() || i>j){
f[i][j] = "";
return f[i][j];
}
if(f[i][j] != null && !"".equals(f[i][j])){
return f[i][j];
}
if(i==j){
f[i][j] = s.substring(i,i+1);
return f[i][j];
}else if(S[i] == S[j] && s.substring(i+1,j).equals(generateLongestPalindromeSubString(f,i+1,j-1,s,S))){
f[i][j] = s.substring(i,j+1);
return f[i][j];
}else {
String s1 = generateLongestPalindromeSubString(f,i+1,j-1, s, S);
String s2 = generateLongestPalindromeSubString(f,i,j-1, s, S);
String s3 = generateLongestPalindromeSubString(f,i+1,j, s, S);
String maxS = s1;
if(maxS.length()<s2.length()){
maxS = s2;
}
if(maxS.length() < s3.length()){
maxS = s3;
}
f[i][j] = maxS;
return f[i][j];
}
}
}
3.动态规划
我们使用 f[i][j] 表示在字符中 i-j 是否是回文子串
- i == j ,f[i][j] = True
- i+1 == j , f[i][j] = s[i] == s[j]
- i+1 < j , f[i][j] = (s[i]==s[j] && f[i+1]f[j-1])
记忆搜索对比动态规划就像是俄罗斯套娃的两种操作,记忆搜索是在一层一层的揭开套娃,动态规划是从短到长,一层一层的套上去
public class Solution {
/**
* @param s: input string
* @return: the longest palindromic substring
*/
public String longestPalindrome(String s) {
// write your code here
char[] S = s.toCharArray();
int sLength = S.length;
boolean[][] f = new boolean[sLength][sLength];
int maxLength = 1;
int start = 0;
for(int j=0;j<sLength;j++){
for(int i=0;i<=j;i++){
if(i==j){
f[i][j]=true;
}else if(j == i+1){
f[i][j] = S[i] == S[j];
}else if(j > i+1){
f[i][j] = (S[i]==S[j] && f[i+1][j-1]);
}
//
if(f[i][j] && (j-i + 1 >maxLength )){
maxLength = j-i+1;
start = i;
}
}
}
return s.substring(start, start + maxLength);
}
}
4.Manacher's Algorithm-马拉车算法
这篇博客写的很好了,想具体了解马拉车算法的可以移步这里 - Manacher's Algorithm-马拉车算法
public class Solution {
/**
* @param s: input string
* @return: the longest palindromic substring
*/
public String longestPalindrome(String s) {
// write your code here
char[] S = s.toCharArray();
StringBuilder res = new StringBuilder("$#");
for (int i=0;i<S.length;i++){
res.append(S[i]).append("#");
}
S = res.toString().toCharArray();
int[] p = new int[S.length];
int mi = 0,right = 1;
int maxPoint = 0,maxLength = 0;
for(int i=1;i<S.length;i++){
p[i] = i < right ? Math.min(p[2*mi-i],right-i) : 1;
while(i+p[i] < S.length && S[i+p[i]] == S[i-p[i]]){
p[i]++;
}
if(i+p[i] > right){
mi = i;
right = i+p[i];
}
if(p[i]>maxLength){
maxPoint = i;
maxLength = p[i];
}
}
return s.substring((maxPoint-maxLength)/2, (maxPoint-maxLength)/2 + maxLength-1);
}
}