###### tags: `Leetcode` # 0005. 最长回文子串 Link: https://leetcode-cn.com/problems/longest-palindromic-substring/ ## 思路 ## Keypoints ## Code in C++ ## Code in Java ```java= 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); for (int i = 0; i < len - 1; i++) { String odd = centerSpread(s, i, i); String eve = centerSpread(s, i, i +1); String tmp = (odd.length() > eve.length()) ? odd : eve; if (tmp.length() > maxLen) { res = tmp; maxLen = res.length(); } } return res; } private String centerSpread(String s, int low, int high) { while (low >= 0 && high < s.length()){ if (s.charAt(low) == s.charAt(high)){ low--; high++; } else break; } return s.substring(low + 1, high); } } ``` ## 思路1 ### 中心扩散 比较容易想到的是枚举可能出现的**回文子串的“中心位置”**,从“中心位置”尝试尽可能扩散出去,得到一个回文串。 因此中心扩散法的思路是:遍历每一个索引,以这个索引为中心,利用“回文串”中心对称的特点,往两边扩散,看最多能扩散多远。 枚举“中心位置”时间复杂度为 $O(N)O$,从“中心位置”扩散得到“回文子串”的时间复杂度为 $O(N)$,因此时间复杂度可以降到 $O(N^2)$。 在这里要注意一个细节:回文串在长度为奇数和偶数的时候,“回文中心”的形式是不一样的。 * 奇数回文串的“中心”是一个具体的字符,例如:回文串 "aba" 的中心是字符 "b"; * 偶数回文串的“中心”是位于中间的两个字符的“空隙”,例如:回文串串 "abba" 的中心是两个 "b" 中间的那个“空隙”。 我们可以设计一个方法,兼容以上两种情况: * 如果传入重合的索引编码,进行中心扩散,此时得到的回文子串的长度是奇数; * 如果传入相邻的索引编码,进行中心扩散,此时得到的回文子串的长度是偶数。 ## 思路2 ### 动态规划 #### 第一步:定义状态 ```dp[i][j]``` 表示子串 ``` s[i..j]``` 是否为回文子串,这里子串 ``` s[i..j]``` 定义为左闭右闭区间,可以取到 ``` s[i]``` 和 ``` s[j]``` 。 #### 第二步:状态转移方程 ``` dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1] ``` #### 第三步:状态初始化 ```dp[i][j]``` 对角线上的元素一定是true #### 第四步:输出 只要一得到 ```dp[i][j] = true```,就记录子串的长度和起始位置,没有必要截取,这是因为截取字符串也要消耗性能,记录此时的回文子串的「起始位置」和「回文长度」即可。 ```java= 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 { if (j - i < 3) { // 表示i和j中间只有一个元素 dp[i][j] = true; } else { 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); } } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up