1. 程式人生 > >【字串】最長連續迴文串(Longest Palindromic Substring)

【字串】最長連續迴文串(Longest Palindromic Substring)

題目: 

Given a string S, find the longest palindromic substring in S.

給出一個字串S,找到一個最長的連續迴文串。

例如串 babcbabcbaccba 最長迴文是:abcbabcba

這個題目小弟給出3中解法,前兩種的都是 O(n^2), 第三種思路是O(n). 

思路1. 動態規劃

這裡動態規劃的思路是 dp[i][j] 表示的是 從i 到 j 的字串,是否是迴文串。

則根據迴文的規則我們可以知道:

如果s[i] == s[j] 那麼是否是迴文決定於 dp[i+1][ j - 1]

當 s[i] != s[j] 的時候, dp[i][j] 直接就是 false。

動態規劃的進行是按照字串的長度從1 到 n推進的。

程式碼很明晰:給出java程式碼,複雜度 O(n^2)

  1. publicclass DPSolution {  
  2.     boolean[][] dp;  
  3.     public String longestPalindrome(String s)  
  4.     {  
  5.         if(s.length() == 0)  
  6.         {  
  7.             return"";  
  8.         }  
  9.         if(s.length() == 1)  
  10.         {  
  11.             return
     s;  
  12.         }  
  13.         dp = newboolean[s.length()][s.length()];  
  14.         int i,j;  
  15.         for( i = 0; i < s.length(); i++)  
  16.         {  
  17.             for( j = 0; j < s.length(); j++)  
  18.             {  
  19.                 if(i >= j)  
  20.                 {  
  21.                     dp[i][j] = true//當i == j 的時候,只有一個字元的字串; 當 i > j 認為是空串,也是迴文
  22.                 }  
  23.                 else
  24.                 {  
  25.                     dp[i][j] = false//其他情況都初始化成不是迴文
  26.                 }  
  27.             }  
  28.         }  
  29.         int k;  
  30.         int maxLen = 1;  
  31.         int rf = 0, rt = 0;  
  32.         for( k = 1; k < s.length(); k++)  
  33.         {  
  34.             for( i = 0;  k + i < s.length(); i++)  
  35.             {  
  36.                 j = i + k;  
  37.                 if(s.charAt(i) != s.charAt(j)) //對字串 s[i....j] 如果 s[i] != s[j] 那麼不是迴文
  38.                 {  
  39.                     dp[i][j] = false;  
  40.                 }  
  41.                 else//如果s[i] == s[j] 迴文性質由 s[i+1][j-1] 決定
  42.                 {  
  43.                     dp[i][j] = dp[i+1][j-1];  
  44.                     if(dp[i][j])  
  45.                     {  
  46.                         if(k + 1 > maxLen)  
  47.                         {  
  48.                             maxLen = k + 1;  
  49.                             rf = i;  
  50.                             rt = j;  
  51.                         }  
  52.                     }  
  53.                 }  
  54.             }  
  55.         }  
  56.         return s.substring(rf, rt+1);  
  57.     }  
  58. }  


思路2. KMP匹配

第二個思路來源於字串匹配,最長迴文串有如下性質: 

對於串S, 假設它的 Reverse是 S', 那麼S的最長迴文串是 S 和 S' 的最長公共字串。

例如 S = abcddca,  S' = acddcba, S和S'的最長公共字串是 cddc 也是S的最長迴文字串。

如果S‘是 模式串,我們可以對S’的所有後綴列舉(S0, S1, S2, Sn) 然後用每個字尾和S匹配,尋找最長的匹配字首。

例如當前列舉是 S0 = acddcba 最長匹配字首是 a

S1  = cddcba 最長匹配字首是 cddc

S2 = ddcba 最長匹配字首是 ddc

當然這個過程可以做適當剪枝,如果當前列舉的字尾長度,小於當前找到的最長匹配,則直接跳過。

Java 程式碼如下:

  1. publicclass Solution {  
  2.     privateint[] next;  
  3.     privatevoid GetNext(String s) //KMP求next陣列
  4.     {  
  5.         int i,j;  
  6.         i = 0;   
  7.         j = -1;  
  8.         next[0] = -1;  
  9.         while( i < s.length())  
  10.         {  
  11.             if( j == -1 || s.charAt(i) == s.charAt(j))  
  12.             {  
  13.                 i++;  
  14.                 j++;  
  15.                 next[i] = j;  
  16.             }  
  17.             else
  18.             {  
  19.                 j = next[j];  
  20.             }  
  21.         }  
  22.     }  
  23.     privateint compare(String pattern, String s) //用KMP演算法做求出最長的字首匹配
  24.     {  
  25.         int i,j;  
  26.         i = 0;  
  27.         j = 0;  
  28.         int maxLen = 0;  
  29.         while( i < s.length())  
  30.         {  
  31.             if(j == -1 || pattern.charAt(j) == s.charAt(i))  
  32.             {  
  33.                 i++;  
  34.                 j++;  
  35.             }  
  36.             else
  37.             {  
  38.                 j = next[j];  
  39.             }  
  40.             if( j > maxLen)  
  41.             {  
  42.                 maxLen = j;  
  43.             }  
  44.             if(j == pattern.length())  
  45.             {  
  46.                 return maxLen;  
  47.             }  
  48.         }  
  49.         return maxLen;  
  50.     }  
  51.     public String longestPalindrome(String s)  //
  52.     {  
  53.         // Start typing your Java solution below
  54.         // DO NOT write main() function
  55.         String reverString = new StringBuilder(s).reverse().toString();  //求得到 輸入string 的reverse
  56.         next = newint[s.length() + 1];  
  57.         String maxPal = "";  
  58.         int maxLen = 0;  
  59.         int len;  
  60.         for(int i = 0; i < s.length(); i++) //列舉所有後綴
  61.         {  
  62.             String suffix = reverString.substring(i);  
  63.             if(suffix.length() < maxLen)  
  64.             {  
  65.                 break;  
  66.             }  
  67.             GetNext(suffix);  
  68.             len = compare(suffix, s);  
  69.             if( len > maxLen)  
  70.             {  
  71.                 maxPal = suffix.substring(0, len);  
  72.                 maxLen = len;  
  73.             }  
  74.         }  
  75.         return maxPal;  
  76.     }  
  77. }  


思路3. 思路來源於此

不過原文的陳述仔細研究了一下,有一些地方讓人著實費解,所以自己決定重寫一遍。

這裡描述了一個叫Manacher’s Algorithm的演算法。

演算法首先將輸入字串S, 轉換成一個特殊字串T,轉換的原則就是將S的開頭結尾以及每兩個相鄰的字元之間加入一個特殊的字元,例如#

例如: S = “abaaba”, T = “#a#b#a#a#b#a#”.

為了找到最長的迴文字串,例如我們當前考慮以Ti為迴文串中間的元素,如果要找到最長迴文字串,我們要從當前的Ti擴充套件使得 Ti-d … Ti+d 組成最長迴文字串. 這裡 d 其實和 以Ti為中心的迴文串長度是一樣的. 進一步解釋就是說,因為我們這裡插入了 # 符號,對於一個長度為偶數的迴文串,他應該是以#做為中心的,然後向兩邊擴,對於長度是奇數的迴文串,它應該是以一個普通字元作為中心的。通過使用#,我們將無論是奇數還是偶數的迴文串,都變成了一個以Ti為中心,d為半徑兩個方向擴充套件的問題。並且d就是迴文串的長度。

例如 #a#b#a#, P = 0103010, 對於b而言P的值是3,是最左邊的#,也是延伸的最左邊。這個值和當前的迴文串是一致的。

如果我們求出所有的P值,那麼顯然我們要的迴文串,就是以最大P值為中心的迴文串。

T = # a # b # a # a # b # a #
P = 0 1 0 3 0 1 6 1 0 3 0 1 0

例如上面的例子,最長迴文是 “abaaba”, P6 = 6.

根據觀察發現,如果我們在一個位置例如 abaaba的中間位置,用一個豎線分開,兩側的P值是對稱的。當然這個性質不是在任何時候都會成立,接下來就是分析如何利用這個性質,使得我們可以少算很多P的值。

下面的例子 S = “babcbabcbaccba” 存在更多的摺疊迴文字串。


C表示當前的迴文中心,L和R處的線表示以C為中心可以到達的最左和最右位置,如果知道這些,我們如何可以更好的計算C後面的P[i].  假設我們當前計算的是 i = 13, 根據對稱性,我們知道對稱的那個下標 i' = 9. 

相關推薦

字串連續Longest Palindromic Substring

題目:  Given a string S, find the longest palindromic substring in S. 給出一個字串S,找到一個最長的連續迴文串。 例如串 babcbabcbaccba 最長迴文是:abcbabcba 這個題目小弟

[國家集訓隊]

可能還是非常板子的\(Manacher\) 還是先跑一遍\(Manacher\)處理出來所有的迴文半徑\(r[i]\) 由於我們要找的答案是兩個迴文串拼了起來,所以我們考慮列舉中間這個拼接處 所以我們要找到每一個\(i\),其左邊能夠到達\(i\)的和右邊能到達\(i\)的最大的迴文半徑 顯然並不能直

Longest Palindromic Substring

題目描述: 給出一個字串(假設長度最長為1000),求出它的最長迴文子串,你可以假定只有一個滿足條件的最長迴文串。 樣例 給出字串 "abcdzdcab",它的最長迴文子串為 "cdzdc"。 挑戰 O(n2) 時間複雜度的演算法是可以接受的,如果你

Longest Palindromic Substring——三種時間複雜度的解法

public String longestPalindrome(String s) {        List<Character> s_new = new ArrayList<>();        for(int i = 0;i < s.length();i++){     

22562 Problem A 字串

問題 A: 【字串】最長迴文子串 時間限制: 1 Sec  記憶體限制: 128 MB 提交: 114  解決: 56 [提交][狀態][討論版][命題人:外部匯入] 題目描述   &nb

bzoj 2565: manacher+線段樹

因為我很愚蠢所以用了很愚蠢的O(nlogn)的manacher+線段樹做法 就是開兩個線段樹mn和mx分別表示左端點在i的最長迴文子串和右端點在i的最長迴文子串 用manacher求出每個點的最長迴文子串,然後對於一組(i,f[i])(這裡的i是加完#之後的串),我們考慮對原串貢獻是對於中點右邊一段迴文串上點

BZOJ 2565 雙回

++ pac const ble char 題目 lan i++ man 【題目鏈接】 https://www.lydsy.com/JudgeOnline/problem.php?id=2565 【算法】 Manacher 【代

給定一個字串,找出其中

迴文 ,是指數或者字串具有首尾迴環性質,從後向前按位顛倒後與原文一樣。首尾迴環的數字就是迴文數,如:121,12321;首尾迴環的字串就是迴文串,如:’madam’。 程式碼 import java.util.LinkedList; import java.util.List;

查詢字串字串

# 採用的是從裡向外擴充套件的方法來計算 def longestPalindrome1(s): res = "" for i in range(len(s)): # 如果長度為奇數的話 tmp = helper(s, i, i)

字串Longest Palindromic Substring

Given a string S, find thelongest palindromic substring in S. You may assume that the maximumlength ofS is 1000, and there exists one uni

Leetcode-字串問題--字串

1.  尋找最長的迴文字串 1)動態規劃問題 利用遞迴和動態規劃來求解 f()為當前字串的最長的迴文字串 f(l,r)=max_size(f(l+1,r),f(l,r-1)) 然後利用遞迴來求解。 缺點:該演算法複雜度較高,實際會需要較長時間 2)leetcode最優解法

找出字串

一個字串有很多子串,這些子串有可能是迴文,那麼,怎麼找出其中最長的迴文子串來呢? 例如: "I like racecars that go fast" -> (racecar)為7 "a" -&

洛谷P4555雙回

namespace n+1 const 枚舉 its 定義 struct 延伸 i++ 題目大意:給定一個長度為 N 的字符串 S,求 S 的最長雙回文子串的長度,雙回文子串定義為是 S 的一個子串,可以分成兩個互不相交的回文子串。 題解:利用回文自動機 len 數組的性質

BZOJ 2565:

一開始以為是要上線段樹的(線段樹解法在這裡就不說了吧QAQ),然後發現有 O(n) O(n)做法,妙啊 https://www.cnblogs.com/CQzhangyu/p/6802572.html #include<bits/stdc++.h>

Tsinsen A1280

找到最長的由連續的兩個迴文串構成的原串的子串,即形如AB,A和B都是迴文串,但AB不一定是迴文串 處理出以每個點為末尾元素的最長迴文串長度,再O(n)找到最長連線串 #include<bits/stdc++.h> #define MOD 10000000

leetcode公共子字首 python3擊敗99.65%使用者

【leetcode】最長公共子字首 python3(擊敗99.65%使用者) #解題分析 列表共有三種情況: 1、列表為空,即[],len(strs) ==0,返回 ’ ’ 2、列表中有為’ ’ 的元素,返回 ’ ’ 3、正常情況 class Solution:

HYSBZ

思路:套用迴文串的模板求出最長迴文串之後, 用兩個陣列L與R分別存 最長迴文串右邊界在當前節點的最長迴文串長度,以及左邊界在當前節點的最長迴文串長度。然後分別在掃描一下兩個陣列,得到迴文串左右邊界在當前節點的最長迴文串長度。然後取最大值就好了。 #include <i

BZOJ 2565 雙回manacher

font www sta oid gre 整數 main 包含 scanf 565: 最長雙回文串 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3343 Solved: 1692[Submit][Status][Disc

BZOJ 2565 雙回自動機

%s sca div can 回文 != printf spa 就是 題意 給一個長度為N的字符串S。對於一個字符串AB,如果A和B都是回文串,那麽稱AB是一個雙回文串。求問S最長雙回文子串的長度?N <= 100000 題解 正反雙向構造回文自動機,得到某一個點為結

找出字串不重複子或者是列表

# 找出來一個字串中最長不重複子串def find_Maxlen_Son_list(astr): maxlen = 0 定義最長字串的初始長度// dict = {} list1 = [] for i in range(0,len(astr)): str2 = ''