0
点赞
收藏
分享

微信扫一扫

LC005-最长回文子串


最长回文子串

给定一个字符串 ​​s​​​,找到 ​​s​​​ 中最长的回文子串。你可以假设 ​​s​​ 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

方法一:暴力匹配法

思路分析:

  • 根据回文子串的定义,美剧所有长度大于等于2的长度,依次判断他们是否是回文
  • 只针对当前得到的最长回文子串长度的子串进行"回文验证".
  • 在记录最长回文子串的时候,可以只记录“当前子串的起始位置”和“子串长度”,不必做截取。

package com.nie.Leec.exercise;

public class lee_005 {
public static void main(String[] args) {
String str = "babad";
lee_005 a = new lee_005();
System.out.println(a.longestPalindrome(str));
}

public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
//先将字符串转换成数组
char[] charArray = s.toCharArray();
for (int i = 0; i < len - 1; i++) { //第一个数子,当开始比如 babad中表示b 一直往后遍历一直到结束
for (int j = i + 1; j < len; j++) {//在一次循环中,babad 第一个遍历到b 第二个循环就有其后紧邻的开始遍历 即b
//j-i+1>maxLen判断是否比保存的长度长,,如果长,,进行判断是否是回文
if (j - i + 1 > maxLen && validPalidromic(charArray, i, j)) {

maxLen = j - i + 1;//目前最长的回文的长度
begin = i;//回文开始的位置
}
}
}
return s.substring(begin, maxLen + begin);
}

private boolean validPalidromic(char[] charArray, int left, int right) {
while (left < right) {
if (charArray[left] != charArray[right]) { //进行截取的
return false;
} else {
left++;
right--;
}
}
return true;
}
}

复杂度分析:

  • 时间复杂度:O(N^3),这里 NN是字符串的长度,枚举字符串的左边界、右边界,然后继续验证子串是否是回文子串,这三种操作都与 NN相关;
  • 空间复杂度:O(1),只使用到常数个临时变量,与字符串长度无关。

方法二:动态规划

主要思想:

主要思想:一旦在一个回文串的两端,对称的加上相同的元素,那么新形成的字符串仍然是一个回文串

方法:设定两个下标,从左到右扫描,一旦i和j下标重回,那么i下标移动的头部,从头开始扫描,j下标向后移动一个元素

当i下标和j下标指向的元素相同的时候,判断除去这两个相同元素剩下的字符串是否是回文串,如果是,那么最长回文串就是当前形成的新串,如果不是则继续扫描.

分布思路

  1. 先考虑题目的特解,本题的特解要考虑字符串为空串和字符串为1的情况,这两种情况都符合题意,所以直接返回字符串s。
  2. 声明两个变量maxLen为最大长度,begin为最大长度的回文的开始位置
  3. 二维数组dp[][],dp[i][j]为下标i到j的位置上的字符为回文 ,初始化的dp[i][i],因为单个字符串必然为回文
  4. 两重循环,j从第二个字符(下标为1)一直遍历到字符串末尾,同时保证 i从0遍历到j-1(i<j)。
  5. dp[i][j]为回文的判定条件第一个就是c[i] == c[j] ,符合这一条件的继续判定,不符合的直接置为false
  6. j-i<3,说明 j-i+1<4, 表明i到j没有4个字符,i到j中间只有0个或者1个字符,上一步已经判定c[i] == c[j],所以dp[i][j]必为回文,置为true。
  7. 边界条件为 j-i>=3,即j-i+1>=4,i到j至少有4个字符。这种情况下dp[i][j]要为回文,dp[i+1][j-1]必须为回文,即dp[i][j] = dp[i + 1][j - 1] 同正同错。
  8. 当dp[i][j]为回文且 i到j的字符串个数大于当前维护的最大长度,即 j-i+1>maxLen时候,需要把maxLen这个最大长度改为i到j的个数(j-i+1),开始位置begin改为i。

public class Solution {

public String longestPalindrome(String s) {
// 特判
int len = s.length();
if (len < 2) {
return s;
}

int maxLen = 1;
int begin = 0;

// dp[i][j] 表示 s[i, j] 是否是回文串
boolean[][] dp = new boolean[len][len];
char[] charArray = s.toCharArray();

for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
//j-i<3,说明 j-i+1<4, 表明i到j没有4个字符,i到j中间只有0个或者1个字符,
//上一步已经判定c[i] == c[j],所以dp[i][j]必为回文,置为true。
if (j - i < 3) {
dp[i][j] = true;
} else {
// j-i>=3,即j-i+1>=4,i到j至少有4个字符。这种情况下dp[i][j]要为回文,
//dp\[i+1]\[j-1]必须为回文,即dp\[i]\[j] = dp\[i + 1]\[j - 1] 同正同错。
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}

复杂度分析:

  • 时间复杂度:O(N^2);
  • 空间复杂度:O(N2),二维数组,因此空间复杂度为O(N2);

总结:

  • 用动态规划解题问题,有的时候并不是直接面向问题的
  • 动态规划依然是(空间换取时间)的思想的体现,而且本身动态规划就是一种打表格,就是空间换取时间
  • 动态规划的本质还是(暴力解法),因为需要枚举左右边界,有O(N2);

方法三:中心扩散法

文串一定是对称的,所以我们可以每次循环选择一个中心,进行左右扩展,判断左右字符是否相等即可。

LC005-最长回文子串_回文串

  • 奇数回文串的“中心”是一个具体的字符,例如:回文串 “aba” 的中心是字符 “b”;
  • 偶数回文串的“中心”是位于中间的两个字符的“空隙”,例如:回文串串 “abba” 的中心是两个 “b” 中间的那个“空隙”。

public class Solution {

public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
String res = s.substring(0, 1);
// 中心位置枚举到 len - 2 即可
for (int i = 0; i < len - 1; i++) {
String oddStr = centerSpread(s, i, i);
String evenStr = centerSpread(s, i, i + 1);
String maxLenStr = oddStr.length() > evenStr.length() ? oddStr : evenStr;
if (maxLenStr.length() > maxLen) {
maxLen = maxLenStr.length();
res = maxLenStr;
}
}
return res;
}

private String centerSpread(String s, int left, int right) {
// left = right 的时候,此时回文中心是一个字符,回文串的长度是奇数//左边
// right = left + 1 的时候,此时回文中心是一个空隙,回文串的长度是偶数
int len = s.length();
int i = left;
int j = right;
while (i >= 0 && j < len) {
if (s.charAt(i) == s.charAt(j)) {
i--;
j++;
} else {
break;
}
}
// 这里要小心,跳出 while 循环时,恰好满足 s.charAt(i) != s.charAt(j),因此不能取 i,不能取 j
return s.substring(i + 1, j);
}
}

复杂度分析

  • 时间复杂度:O(N2),枚举"中心位置"时间复杂度O(N),由中心位置扩展到回文O(N),所以为O(N2)
  • 空间复杂度O(1),只使用到常数个临时变量,与字符串长度无关。


举报

相关推荐

0 条评论