lintcode最長回文子串(Manacher算法)

題目來自lintcode, 鏈接:http://www.lintcode.com/zh-cn/problem/longest-palindromic-substring/

最長回文子串?

給出一個字符串(假設長度最長為1000),求出它的最長回文子串,你可以假定只有一個滿足條件的最長回文串。

樣例

給出字符串?"abcdzdcab",它的最長回文子串為?"cdzdc"

挑戰

O(n2) 時間復雜度的算法是可以接受的,如果你能用 O(n) 的算法那自然更好。

?

一. 首先給出O(n^2)的算法

思路:dp[i][k]表示第i個位置開始長度為k的串的最大回文串的長度, j=i+k-1

   ? 當?s[i] == s[j] && dp[i+1][k-2] == k-2, ?dp[i][k] = dp[i+1][k-2] + 2;

   ? 否則 ?dp[i][k] = max(dp[i+1][k-1], dp[i][k-1]);

   ? 并記錄dp[i][k]的最大值,最后找到最長回文子串的區間。

    int dp[1005][1005];string longestPalindrome(string& s) {// Write your code hereO(n^2)int len = s.size();memset(dp, 0, sizeof(dp));for(int i=0; i<len; ++i)dp[i][1] = 1;int ld=0, rd=0, maxL = 1;for(int k=2; k<=len; ++k){for(int i=0, j; i<len && (j=i+k-1)<len; ++i){if(s[i] == s[j] && dp[i+1][k-2] == k-2)dp[i][k] = dp[i+1][k-2] + 2;else dp[i][k] = max(dp[i+1][k-1], dp[i][k-1]);if(maxL<dp[i][k]){maxL = dp[i][k];ld = i;rd = j;}}}return s.substr(ld, rd-ld+1);
}

二.然后看一下復雜度為O(n)的Manacher算法

2.1 先說一下這個算法的思想:

  用一個數組 P[i] 來記錄以字符S[i]為中心的最長回文子串向左/右擴張的長度(包括S[i])。

2.2 算法基本要點:

  這個算法不能求出最長回文串長度為偶數回文串。用一個非常巧妙的方式,將所有可能的奇數/偶數長度的回文子串都轉換成了奇數長度:在相鄰兩個字符之間插入一個特殊的符號。比如 abba 變成 a#b#b#a。 為了進一步減少編碼的復雜度,可以在字符串的開始加入另一個特殊字符,這樣就不用特殊處理越界問題,比如$a#b#b#a。

2.3 具體說一個例子:

  S[] ? ? 1? #? 2? #? 2? #? 1? #? 2? #? 3? #? 2? #? 1
  P[] ? ? 1 ?1 ? 2 ?1 ?2 ? 1 ?4 ?1 ?2 ?1 ?5 ?1 ? 2 ?1 ?1
  (p.s. 可以看出,P[i]-1正好是原字符串中回文串的總長度)

2.4 為啥要對字符串進行處理(插入'#')

如果不對字符進行處理, 對于最長回文串為偶數的情況下:

  S[] ? ? 1 ?2 ?1 ?1 ?2 ?1
  P[] ? ? 1 ?2 ?1 ?1 ?2 ?1

對字符進行處理,對于最長回文串為偶數的情況下:

  S[] ? ? 1 ?# ?2 ?# ?1 ?# ?1 ?# ?2 ?# ?1
  P[] ? ? 1 ?1 ? 3 ?1 ?2 ?6 ? 2 ?1 ?3 ?1 ?1

可見不對字符進行處理,對于最長回文串為偶數的情況是不能得到最大的回文串的長度。

2.5 如何計算P數組的值:

  算法增加兩個輔助變量id和mx,其中id表示最大回文子串中心的位置,mx則為id+P[id],也就是最大回文子串的邊界。

  當 mx - i > P[j] 的時候,以S[j]為中心的回文子串包含在以S[id]為中心的回文子串中,由于 i 和 j 對稱,以S[i]為中心的回文子串必然包含在以S[id]為中心的回文子串中,所以必有 P[i] = P[j],見下圖。

              

  當 P[j] > mx - i 的時候以S[j]為中心的回文子串不完全包含于以S[id]為中心的回文子串中,但是基于對稱性可知,下圖中兩個綠框所包圍的部分是相同的,也就是說以S[i]為中心的回文子串,其向右至少會擴張到mx的位置,也就是說 P[i] >= mx - i。至于mx之后的部分是否對稱,就只能一個一個匹配了。

              

  對于 mx <= i 的情況,無法對 P[i]做更多的假設,只能P[i] = 1,然后再去匹配了。

2.6 尋找最大長度的回文子串:

  看一種情況:

    S[] ? ? 1 ?# ?2 ?# ?2 ?
    P[] ? ? 1 ?1 ? 2 ?2 ?1?

  首先, P中的最大值為2,但是最大值有兩個,我們應該選擇哪一個?其實,如果P中的最大值對應的字符不是'#',顯然不能得到最大長度的回文串。所以當我們遇到這種情況時(maxP == P[i] && S[i]=='#')要更新最大值所在位置。

2.7 最后代碼:

class Solution {
public:/*** @param s input string* @return the longest palindromic substring*/string manacher(string& str){int *p = new int[str.size()]();memset(p, 0, sizeof(p));int mx = 0, id = 0;for(int i=1; i<str.size(); i++){if(mx > i)p[i] = min(p[2*id-i], mx-i);elsep[i] = 1;while(str[i - p[i]] == str[i + p[i]])++p[i];if(i + p[i] > mx){mx = i + p[i];id = i;}}//尋找數組P中的最大值的位置int maxP = 0;for(int i=1; i<str.size(); ++i)if(maxP < p[i] || (maxP == p[i] && str[i]=='#')){maxP = p[i];id = i;} //根據id,確定最長回文串的區間int ld = id-p[id]+1, rd = id+p[id]-1;string ans = "";for(int i=ld; i<=rd; ++i)if(str[i]!='#')ans += str[i];return ans;}string longestPalindrome(string& s) {// Write your code here//采用manacher算法,O(n)的時間復雜度int len = s.size();
//首先預處理字符串,每兩個字符之間插入'#'
int k = -1;for(int i=1; i<len; ++i)s.insert(k+=2, 1, '#');s.insert(0, 1, '$');return manacher(s);} };

?

轉載于:https://www.cnblogs.com/hujunzheng/p/5033891.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531375.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531375.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531375.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

全排列總結

接觸全排列已經好長時間了&#xff0c;一直沒有抽空總結一下全排列的相關問題&#xff0c;下面來說一下&#xff01; 排列 一般地&#xff0c;從n個不同元素中取出m&#xff08;m≤n&#xff09;個元素&#xff0c;按照一定的順序排成一列&#xff0c;叫做從n個元素中取出m個元…

大小端問題傻傻分不清?

先來熟悉一下概念&#xff1a; 大端&#xff1a;數據的高位數據保存在低位地址&#xff0c;數據的低位數據保存在高地址 小端&#xff1a;數據的高位數據保存在高位地址&#xff0c;數據的低位數據保存在低地址為什么會存在大小端的問題&#xff1f; 這是因為在計算機系統中&a…

n個結點,不同形態的二叉樹(數目+生成)

題目鏈接&#xff1a; 不同的二叉查找樹&#xff1a;http://www.lintcode.com/zh-cn/problem/unique-binary-search-trees/ 不同的二叉查找樹 II&#xff1a;http://www.lintcode.com/zh-cn/problem/unique-binary-search-trees-ii/ 不同形態二叉樹的數目&#xff1a; 樣例 給出…

c++ stringstream(老好用了)

前言&#xff1a; 以前沒有接觸過stringstream這個類的時候&#xff0c;常用的字符串和數字轉換函數就是sscanf和sprintf函數。開始的時候就覺得這兩個函數應經很叼了&#xff0c;但是畢竟是屬于c的。c中引入了流的概念&#xff0c;通過流來實現字符串和數字的轉換方便多了。在…

mount --bind的用處

&#xff08;一&#xff09;mount --bind介紹 mount --bind的作用是將兩個目錄連接起來&#xff0c;例如&#xff1a;mount ---bind /dir1 /dir2 是將dir1目錄掛載到dir2目錄上&#xff0c;下面來實際演示一下&#xff1a; 上面的操作中首先創建了dir1 dir2兩個目錄&#xf…

gcc -strip編譯選項的作用

從字面上來看strip的意思是脫衣服、拆卸&#xff0c;那么gcc --strip的作用大概能猜錯來了。 沒錯就是有選擇地除去行號信息、重定位信息、調試段、typchk 段、注釋段、文件頭以及所有或部分符號表。 一旦使用該命令&#xff0c;則很難調試文件的符號&#xff0c;因此&#x…

lintcode 落單的數(位操作)

題目1 落單的數 給出2*n 1 個的數字&#xff0c;除其中一個數字之外其他每個數字均出現兩次&#xff0c;找到這個數字。 鏈接&#xff1a;http://www.lintcode.com/zh-cn/problem/single-number/ 樣例 給出 [1,2,2,1,3,4,3]&#xff0c;返回 4 挑戰 一次遍歷&#xff0c;常數級…

旋轉圖像

旋轉圖像 給定一個NN的二維矩陣表示圖像&#xff0c;90度順時針旋轉圖像。 看個例子 算法1&#xff1a; 如上圖所示&#xff0c;設一個N階二維矩陣&#xff0c;則將矩陣從外向里可以分成N/2個圈&#xff0c;例如&#xff08;1 2 3 4 8 12 16 15 14 13 9 5&#xff09;這是最外邊…

嵌入式開發板模擬器:QEMU

前兩天看微信公眾號時發現了一個嵌入式模擬器&#xff0c;感覺很不錯&#xff0c;自己動手安裝了一個&#xff0c;折騰了幾天&#xff0c;下載一直是個問題&#xff0c;特此記錄如下 模擬器大家應該都聽說過&#xff0c;有的小伙伴打游戲也會安裝模擬器&#xff0c;今天我們介紹…

gcc: weak_alias如何使用

本文主要說明weak和alias是什么和如何使用它 __attribute__是用來說明函數的屬性&#xff0c;weak和alias分別是兩個屬性。 &#xff08;一&#xff09;強符號和弱符號&#xff1a; 強符號&#xff1a;已經初始化的全局變量和未被weak修飾的函數弱符號&#xff1a;未初始化的全…

靜態Include和動態Include測試并總結

主要代碼 hjzgg.css .center-div{width:auto;margin-left: 40%;margin-right: 40%;display: block;position: absolute;top:0px;left:0px; }.text-div{margin-top: 80px; }.hjzgg-div{color:transparent;font-size:20px;font-weight: bold;letter-spacing:2px;-webkit-animatio…

linux終端常用快捷鍵

CTRLALTT 打開終端 CTRLD 關閉終端 CTRL SHIFT "" 放大終端字體 CTRL “-” 縮小終端字體 CTRL r 查找歷史命令 CTRLu 刪除光標前面所有內容 CTRLw 刪除光標左邊的單詞 CTRL k 刪除光標后面的所有內容 CTRLL 清除當前屏幕內容 CTRLa 光標移到開始位置 CTRLe 光標移到…

ueditor的配置和使用

ueditor下載好之后直接復制到項目的WebContent目錄下&#xff0c;并將ueditor\jsp\lib下的jar包復制或者剪切到項目的lib目錄下。先看一下效果&#xff0c;如下&#xff1a; 1.文件的上傳 首先在ueditor/jsp目錄下找到config.json文件&#xff0c;就拿Image上傳來說吧。 "…

windows上搭建NFS服務器

在進行嵌入式開發的時候&#xff0c;我們常用的做法是搭建NFS服務器&#xff0c;然后使把文件系統、調試程序放在NFS服務器上&#xff0c;這樣可以方便調試&#xff0c;以前都是在linux里面開啟NFS服務器&#xff0c;今天來說下window里的nfs服務器–haneWin 一、軟件安裝和使…

計算機是如何啟動的?從未上電到操作系統啟動

計算機是如何啟動的&#xff0c;網絡上很多博文1都從 BIOS 程序的加載開始說起&#xff0c;有的也跳到 BIOS 程序加載 Bootloader 階段。個人認為把這個過程稱為操作系統是如何被加載并啟動應該更加貼切一點。同時&#xff0c;也有計算機硬件大神的文章[1][5]詳細分析計算機加電…

Hibernate注解

前言&#xff1a; 最近正在學習Hibernate通過注解&#xff08;annotation&#xff09;來管理映射關系&#xff0c;以前都是通過XML映射文件。下面拿個小例子說一下。 數據庫物理模型&#xff1a; 數據庫的描述&#xff1a; 一篇博客隨筆可以分到不同的類中&#xff0c;一個類中…

js表單動態添加數據并提交

情景1&#xff1a;已經存在form對象了&#xff0c;動態為form增加對象并提交 function formAppendSubmit(){var myform$(#newArticleForm); //得到form對象var tmpInput$("<input typetext nameblogArticleForm.articleContent/>");tmpInput.attr("value&…

*++p和*p++的區別

首先你應該明白* 和 的優先級是相同的&#xff0c;而且他們的結合性是從又往左的 #include <stdio.h>int main(int argc ,char * argv[]) {int str[]{1,2,3,4,5,6,7,8,9,10};int *p str;int a *p;//a*p ,pp1即a1&#xff0c;p&str[1]int b *p;//pp1,b*p即p&s…

zyUpload+struct2完成文件上傳

前言&#xff1a; 最近在寫自己的博客網站&#xff0c;算是強化一下自己對s2sh框架的理解。期間遇到了很多問題&#xff0c;這些問題在寫之前都考慮過&#xff0c;感覺也就是那樣吧。但正真遇到了&#xff0c;也挺讓人難受的。就利用zyUpload這個js插件實現文件的上傳&#xff…

gbd的簡單使用(一)

這篇文章將gdb的簡單使用&#xff0c;通過此篇文章你能學習到使用gdb進行調試程序 在Linux中編寫程序時&#xff0c;如何進行程序的debug工作呢&#xff1f;今天來介紹下gdb這個工具&#xff0c;可以在Linux下直接man gdb查看幫助信息 &#xff08;一&#xff09;gdb命令介紹 …