題目描述
給你一個字串 s,找到 s 中最長的迴文子串。
例子
示例 1:
輸入:s = "babad"
輸出:"bab"
解釋:"aba" 同樣是符合題意的答案。
示例 2:
輸入:s = "cbbd"
輸出:"bb"
示例 3:
輸入:s = "a"
輸出:"a"
示例 4:
輸入:s = "ac"
輸出:"a"
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/probl...,著作權歸領釦網路所有。
思路以及解答
暴力破解
暴力破解,即是針對裡面每一個子串,都去判斷是否為迴文串。
判斷每一個字元是不是迴文串,比如用 cbac
判斷,左右兩個指標,對稱判斷,相等則往中間移動,繼續判斷,不相等則直接返回 false 。
public static String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return s;
}
String result = s.substring(0, 1);
for (int i=0; i < s.length() - 1; i++) {
for (int j = i + 1; j < s.length(); j++) {
if (judge(s, i, j) && j - i + 1 > result.length()) {
result = s.substring(i, j+1);
}
}
}
return result;
}
// 判斷每個子串是不是迴文
public static boolean judge(String source, int start, int end) {
// 對稱軸對比
while (start <= end) {
if (source.charAt(start) != source.charAt(end)) {
return false;
}
start++;
end--;
}
return true;
}
暴力破解複雜度過高,會超時,不推薦使用。
中心擴充法
迴文串總是中心對稱的,前面使用暴力法的時候,都是擷取出子串之後再判斷,只有判斷到全部對稱,才能證明迴文,這樣其實走了很多彎路,只要最後一個不對稱,前功盡棄。
反過來想,我們不如在每一個點,都嘗試往兩邊擴充,這樣只要不匹配,就可以及時止順。
值得注意的是,中心擴充法的中心怎麼找?3個字元有多少箇中心呢?
一共有五個中心,有些中心可能是兩個字元的間隙,有些中心可能是字元。那麼設計的時候,我們用 left
和 right
表示兩個指標:
left = right
:對稱中心為字元left + 1 = right
: 對稱中心為兩個字元的間隙
具體實現如下:
class Solution {
// 開始下標
public static int start = -1;
// 最大長度
public static int maxLen= 0;
public String longestPalindrome(String s) {
start = -1;
maxLen = 0;
if(s==null||s.length()==0){
return "";
}
for(int i=0;i<s.length();i++){
// 以當前字元為對稱軸
judge(s,i,i);
// 以當前字元和下一個字元的間隙為對稱軸
judge(s,i,i+1);
}
if(start == -1){
return "";
}
return s.substring(start,start+maxLen);
}
public void judge(String s,int left,int right){
while(left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){
left--;
right++;
}
int size = right-left-1;
if(size > maxLen){
maxLen = size;
start = left+1;
}
}
}
動態規劃
其實,一個字串是迴文串的話,那麼它倒過來讀也是一樣的,也就是說,它與它反轉後的字串,其實是完全匹配的,那麼要是我們用一個字串和它反轉字串一一統計匹配,是不是就可以得到結果呢?
答案是肯定的!假設原字串為 s1
,反轉後的字串為 s2
,字串長度為 n
,我們用陣列 nums[n][n]
來記錄匹配的數量,nums[i][j]
表示以 s1[i]
結尾的字元子串,和以 s2[j]
結尾的字元子串,兩者的匹配字元的最大數值。
當
s1[i] == s2[j]
:- 如果
i == 0
或者j == 0
:nums[i][j] = 1
- 否則
nums[i][j] = nums[i - 1][j - 1] + 1;
- 如果
- 如果
s1[i] != s2[j]
,則nums[i][j]=0
前面說的其實就是狀態轉移表示式,也就是 nums[i][j]
是怎麼求解的?nums[i][j]
是依賴於 nums[i - 1][j - 1]
和 當前字元是否匹配,如果當前字元不匹配,直接賦值為 0,只有在當前字元匹配的情況下,才會需要看前面一位的匹配數值 nums[i - 1][j - 1]
。
假設以 babad
為例子:
最後兩行的計算:
實現的程式碼如下:
class Solution {
public static String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return "";
}
if (s.length() == 1) {
return s;
}
int len = s.length();
String s1 = new StringBuffer(s).reverse().toString();
int[][] nums = new int[len][len];
int end = 0, max = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length; j++) {
if (s1.charAt(i) == s.charAt(j)) {
if (i == 0 || j == 0) {
nums[i][j] = 1;
} else {
nums[i][j] = nums[i - 1][j - 1] + 1;
}
}
if (nums[i][j] > max) {
if (len - i - 1 + nums[i][j] - 1 == j) {
end = j;
max = nums[i][j];
}
}
}
}
return s.substring(end - max+1, end+1);
}
}
作者簡介
【作者簡介】:
秦懷,公眾號【秦懷雜貨店】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。個人寫作方向:Java原始碼解析
,JDBC
,Mybatis
,Spring
,redis
,分散式
,劍指Offer
,LeetCode
等,認真寫好每一篇文章,不喜歡標題黨,不喜歡花裡胡哨,大多寫系列文章,不能保證我寫的都完全正確,但是我保證所寫的均經過實踐或者查詢資料。遺漏或者錯誤之處,還望指正。