怎么判斷一個字符串的最長回文子串是否在頭尾_LeetCode 5 迅速判斷回文串的Manacher算法...

e7e6b6d6cf9187a99adc972e1c2907e7.png

本文始發于個人公眾號: TechFlow

題意

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Link: https://leetcode.com/problems/longest-palindromic-substring/

翻譯

給定一個字符串s,要求它當中的最長回文子串。可以假設s串的長度最大是1000。

樣例

Example 1:
Input: "babad"Output: "bab"Note: "aba" is also a valid answer.Example 2:
Input: "cbbd"Output: "bb"

分析

雖然LeetCode里給這道題的難度是Medium,但實際上并不簡單,我們通過自己思考很難想到最佳解法。
我們先把各種算法放在一邊,先從最簡單的方法開始。最簡單的方法當然是暴力枚舉,但是這道題和之前的字符串問題不同。

我們在暴力枚舉的時候,并不需要枚舉所有的起始位置,再判斷這個子串是否回文。實際上我們可以利用回文串兩邊相等的性質,直接枚舉回文串的中心位置,如果兩邊相等就往兩邊延伸。這樣我們最多需要枚舉n個回文中心,每次枚舉最多遍歷n次。所以最終的復雜度是O(n^2)。


有經驗的同學看到這個復雜度就能反應過來,這明顯不是最優解法。但是對于當前問題,暴力枚舉固然不是最佳解法,但其實也算得上是不錯了,并沒有我們想的那么糟糕,不信的話,我們來看另一個看起來高端很多的解法。

動態規劃(DP)

這道題中利用回文串的性質還有一個trick,對于一個字符串S,如果我們對它進行翻轉,得到S_,顯然它當中的回文子串并不會發生變化。所以如果我們對翻轉前后的兩個字符串求最長公共子序列的話,得到的結果就是回文子串。

算法導論當中對這個問題的講解是使用動態規劃算法,即是對于字符串S中所有的位置i和S_中所有的位置j,我們用一個dp數組記錄下以i和j結尾的S和S_的子串能夠組成的公共子序列的最大的結果。

顯然,對于i=0,j=0,dp[i][j] = 0(假設字符串下標從1開始)

我們寫出DP的代碼:

for 


我們不難觀察出來,這種解法的復雜度同樣是O(n^2)。并且空間復雜度也是O(n^2),也就是說我們費了這么大勁,并沒有起到任何優化。所以從這個角度來看,暴力搜索并不是這題當中很糟糕的解法。

分析到了這里,也差不多了,下面我們直接進入正題,這題的最佳解法,O(n)時間內獲取最大回文子串的曼徹斯特算法。

曼切斯特算法


回文串除了我們剛剛提到的性質之外,還有一個性質,就是它分奇偶。簡而言之,就是回文串的長度可以是奇數也可以是偶數。如果是奇數的話,那么回文串的回文中心就是一個字符,如果是偶數的話,它的回文中心其實是落在兩個字符中間。


舉個例子:


ABA和ABBA都是回文串,前者是奇回文,后者是偶回文


這兩種情況不一致,我們想要一起討論比較困難,為了簡化問題,我們需要做一個預處理,將所有的回文串都變成奇回文。怎么做呢,其實很簡單,我們在所有兩個字符當中都插入一個特殊字符#。


比如:


abba -> #a#b#b#a#


這樣一來,回文中心就變成中間的#了。我們再來看原本是奇回文的情況:


aba -> #a#b#a#


回文中心還是在b上,依然還是奇回文。


預處理的代碼:

def 

曼切斯特算法用到三個變量,分別是數組p,idx和mr。我們接下來一個一個介紹。

首先是數組radis,它當中存在的是每個位置能構成的最長回文串的半徑。注意,這里不是長度,是半徑

我們舉個例子:

字符串S     # a # b # b # a #
radis      1 2 1 2 5 2 1 2 1


我們先不去想這個radis數組應該怎么求,我們來看看它的性質。


首先,i位置的回文串的半徑是radis[i],那么它的長度是多少?很簡單: radis[2] * 2 - 1。那么,這個串中去掉#之后剩下的長度是多少?也就是說預處理之前的長度是多少?


答案是radis[i] - 1,推算也很簡單,總長度是radis[i] * 2 - 1,其中#比字母的數量多一個,所以原串的長度是(radis[i] * 2 - 1 - 1)/2 = radis[i] - 1。


也就是說原串的長度和radis數組就算是掛鉤了。


idx很好理解,它就是指的是數組當中的一個下標,最后是mr,它是most_right的縮寫。它記錄的是在當前位置i之前的回文串所向右能延伸到的最遠的位置。


聽起來有些拗口,我們來看個例子:

ae291d7ed28091fcb5800b5d62a97cfe.png

此時i小于mr,mr對應的回文中心是id。那么i在id的回文范圍當中,對于i而言,我們可以獲取到它關于id的對稱位置:id * 2 - i,我們令它等于i_。知道這個對稱的位置有什么用呢?很簡單,我們可以快速的確定radis[i]的下界。在遍歷到i的時候,我們已經有了i_位置的結果。通過i_位置的結果,我們可以推算i位置的范圍。

radis[i] >= min(radis[i_], mr-i)

為什么是這個結果呢?

我們把情況寫全,假設mr-i > radis[i_]。那么i_位置的回文串全部都落在id位置的回文串里。這個時候,我們可以確定radis[i]=radis[i_]。為什么呢?

因為根據對稱原理,如果以i為中心的回文串更長的話,我們假設它的長度是radis[i_]+1。會導致什么后果呢?如果這個發生,那么根據關于id的對稱性,這個字符串關于id的對稱位置也是回文的。那么radis[i_1]也應該是這么多才對,這就構成了矛盾。如果你從文字描述看不明白的話,我們來看下面這個例子:

S:       c a b c b d b c b a 
cradis:    x_  i_  5   i   x

在這個例子當中,mr-i=5,radis[i_]=2。所以mr - i > radis[i_]。如果radis[i]=3,那么x的位置就應該等于id的位置,同理根據對稱性,x_的位置也應該等于id的位置。那么radis[i_]也應該是3。這就和它等于2矛盾,所以這是不可能出現的,在mr距離足夠遠的情況下,radis[i_]的值限制了i位置的可能性。


我們再來看另一種情況,如果mr - i < radis[i_]時會怎么樣呢?


在這種情況下,由于mr距離i太近,導致i對稱位置的半徑無法在i位置展開。但是mr的右側可能還存在字符,這些字符可以構成新的回文嗎?

字符串S     XXXXXXXXSXXXXXXXXXXXXXXX
radis        i_    id    i mr


也就是說S[mr+1]會和S[i*2-mr-1]的位置相同嗎?
其實我們可以不用判斷就可以知道答案,答案是不會。
我們來看圖:

a30f5d4cac12c40cb0a6e6bffde42eab.png


根據對稱性,如果mr+1的位置對于i可以構成新的對稱。由于radis[i_] > mr-i,也就是說對于i_位置而言,它的對稱范圍能夠輻射到mr對稱點的左邊。我們假設這個地方的字母是a,根據對稱性,我們可以得出mr+1的位置也應該是a。如此一來,這兩個a又能構成新的對稱,那么id位置的半徑就可以再拓展1,這就構成了矛盾。所以,這種情況下,由于mr-i的限制,使得radis[i]只能等于mr - i。

那什么情況下i位置的半徑可以繼續拓展呢?


只有mr - i == radis[i_]的時候,id構成的回文串的左側對于i_可能構不成新的回文,但是右側卻存在這種可能性。

3d8d76bde41e7a86af04ef6f699ff0c0.png

在上圖這個例子當中,i_的位置的回文串向左只能延伸到ml,因為ml-1的位置和關于i_對稱的位置不相等。對于mr的右側,它完全可以既和i點對稱,又不會影響raids[id]的正確性。這個時候,我們就可以通過循環繼續遍歷,拓展i位置的回文串。

整個過程的分析雖然很多,也很復雜,但是寫成代碼卻并不多。

# 初始化

到這里,曼切斯特算法就算是實現完了。雖然我們用了這么多篇幅去介紹它,可是真正寫出來,它只有幾行代碼而已。不得不說,實在是非常巧妙,第一次學習可能需要反復思考,才能真正理解。

不過我們還有一個問題沒有解決,為什么這樣一個兩重循環的算法會是O(n)的復雜度呢?

想要理解這一點,需要我們拋開所有的虛幻來直視本質。雖然我們并不知道循環進行了多少次,但是有兩點可以肯定。通過這兩點,我們就可以抓到復雜度的本質。

第一點,mr是遞增的,只會變大,不會減小。

第二點,mr的范圍是0到n,每次mr增加的數量就是循環的次數。

所以即使我們不知道mr變化了多少次,每次變化了多少,我們依然可以確定,這是一個O(n)的算法。

到這里,文章的內容就結束了,如果喜歡的話,請點個關注吧~

c5380a0842945b334af0724c39d1a1b0.png

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

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

相關文章

linux內核沒有iobuf,LINUX2.6.26.6內核下雙口RAM的驅動函數測試成功!

驅動程序&#xff1a;#include //#include #include #include #include #include #include #include //#include #include //#include #include #include #include #include //#include #include #include #include #include #include #include #include MODULE_LICENSE("…

spring和mybatis結合做簡單的增刪查改系統_springbootamp;amp;vue簡單的景點信息管理系統...

springboot&&vue簡單的景點信息管理系統這兩天閑著沒有什么事&#xff0c;就根據陳哥的教程&#xff0c;試著寫了一個springboot和vue的簡單的景點信息管理系統。也就大致實現了最基本的增刪查改。先看看效果圖吧&#xff1a;1、登陸界面&#xff1a; 2、注冊界面&…

linux 內核 丟棄分片包,LINUX內核關于IP分片重組問題請教

最近研究學習IP分片重組&#xff0c;也拜讀了不少dx的閱讀理解。可還是有疑問&#xff0c;請教xdm。源代碼&#xff1a;linux-2.4.26\linux-2.4.26\net\ipv4\ip_fragment.cIP分片的重組大概經過以下幾個函數:0/ ip_defrag1/ ip_find-->ip_frag_create-->ip_frag_intern2/…

spark算子_十、Spark之詳解Action類算子

常用Action類算子列表reduce(func): 通過func函數來對RDD中所有元素進行聚合運算&#xff0c;先運算分區內數據&#xff0c;再運算分區間數據。scala> val rdd1 sc.makeRDD(1 to 100)rdd1: org.apache.spark.rdd.RDD[Int] ParallelCollectionRDD[4] at makeRDD at :24# 對…

linux 庫函數 劫持,Linux hook技術之-Ring3下動態鏈接庫.so函數劫持

劫持普通函數當然沒有什么意思了&#xff01;我們要劫持的是系統函數&#xff01;我們知道&#xff0c;Unix操作系統中對于GCC而言&#xff0c;默認情況下&#xff0c;所編譯的程序中對標準C函數(fopen、printf、execv家族等等函數)的鏈接&#xff0c;都是通過動態鏈接方式來鏈…

await原理 js_「速圍」Node.js V14.3.0 發布支持頂級 Await 和 REPL 增強功能

本周&#xff0c;Nodejs v14.3.0 發布。這個版本包括添加頂級 Await、REPL 增強等功能。REPL 增強通過自動補全改進對 REPL 的預覽支持&#xff0c;例如&#xff0c;下圖中當輸入 process.ver 之后&#xff0c;不需要輸入剩下的實際內容&#xff0c;它幫我們生成了自動補全的輸…

在linux安裝requests庫命令,在Linux--Ubuntu18.04環境下安裝requests庫

之前在服務器上裝過requests庫&#xff0c;但是記憶中花了好大的力氣才成功&#xff0c;現在因為一次意外&#xff0c;服務器重裝系統&#xff0c;現在這些亂七八糟的庫又要重裝一遍&#xff0c;與上次不同的是&#xff0c;這次我裝一遍就成功了。現在分享一下成功的經歷。Pyth…

linux輸入ls后不顯示_零基礎學習之Linux基礎命令小結

安裝完重啟后&#xff0c;沒有像sery所說在圖形界面崩潰了&#xff0c;由于我沒有安裝X-WINDOWS而是直接進入了文本界面。如果你想做linux管理的話&#xff0c;最好在文本界面下工作&#xff0c;這樣會適應如下圖:第一行顯示的是我們所安裝的linux是Red Hat 企業4第二行顯示的是…

redhat enterprise linux 哪個版本好,Red Hat Enterprise Linux 版本顯示中(Santiago)是啥意思?...

樓主的邏輯還有問題。1、linux跟windows都是一種操作系統&#xff0c;但是它用的分區格式是ext3的&#xff0c;ntfs和fat都不合適。安裝過程中你可以自己選擇刪除現有分區創建新分區&#xff0c;但如果你不了解&#xff0c;很可能把所有的分區都清了。2、redhat分區多大合適看你…

.gitignore文件_【第1739期】為Git倉庫里的.idea文件夾正名

前言.idea該不該提交到代碼倉庫中呢&#xff1f;你的意見呢&#xff1f;今日早讀文章由《Flask Web開發》作者李輝分享。正文從這開始&#xff5e;&#xff5e;在網絡上&#xff0c;我曾多次看到人們對于Git倉庫中的.idea文件夾的偏見。最近的一次是在某個博客中技術專家對于志…

監控linux時間不對,shell 計算故障時間 配合web監控

#!/bin/bash#checkfail.log 為SHELL監控網站時間存放的日志文件 https://blog.51cto.com/junhai/2437965fail_time(){starttimetail -n 1000 checkfail.log |grep "$url"|grep "第1次"|tail -n 3|head -n 1|awk {print $1, $2} #取網站掛掉的時間endtimet…

linux redis清空數據恢復,Redis數據恢復--誤刪數據后一次嚇尿的經歷

1、起因&#xff0c;一個flushdb命令因為誤操作&#xff0c;輸入了一個flushdb命令&#xff0c;導到redis里0號庫里的數據全部清空&#xff0c;OMG&#xff0c;這里有不少重要信息&#xff0c;如果被領導知道&#xff0c;必開除2、appendonly留有生機仔細想想&#xff0c;當時數…

c語言 枚舉類型 uint32_淺談C語言枚舉類型 | 附自創用法分享

經濟學家說過&#xff0c;路邊是不會有100元的&#xff1b;但如果有&#xff0c;你還是要撿起來。同理&#xff0c;在貌似萬物免費的網絡時代&#xff0c;你是很難找到有針對性的好資料&#xff1b;但是如果有&#xff0c;希望你能認真學習吸收。比如筆者今天寫的這一篇一今天這…

linux在bin下加入ssh,移植?ssh?到開發板

2》編譯/home/arm下新建目錄sshwork&#xff0c;并且將源碼復制到該目錄下mkdir /home/arm/sshworkcp zlib-1.2.3.tar.gz openssl-0.9.8d.tar.gz openssh-4.6p1.tar.gz/home/arm/sshwork/home/arm/sshwork下新建目錄lib&#xff0c;用來保存生成的庫文件。mkdir /home/arm/sshw…

java pdf增刪改查_如何利用Java代碼操作索引庫?

今天是劉小愛自學Java的第161天。感謝你的觀看&#xff0c;謝謝你。學習計劃安排如下&#xff1a;學了幾天的Elasticserch&#xff0c;但都是它本身的知識點&#xff0c;如何通過Java語言去操作它呢&#xff1f;這就好比以前學數據庫&#xff0c;在數據庫工具中通過sql語句也能…

linux shell 第幾行,Linux shell 獲得字符串所在行數及位置

shell 獲得字符串所在行數及位置01 獲取字符串所在的行數方式一&#xff1a;用grep -n[rootroot]# cat testapplebitcreatedelectexeflowgood[rootroot]# cat test | grep -n exe5:exe[rootroot]# cat test | grep -n exe | awk -F ":" {print $1}5方式二&#xff1a…

sublime text3 怎么配置、運行python_SublimeText3按ctrl+b執行python無反應

最后更新時間&#xff1a;2017-09-14 現象&#xff1a; 在Sublime中打開.py文件&#xff0c;按”ctrlb”執行時無反應。點擊工具->編譯系統中已經有且識別到Python&#xff0c;但執行”run&#xff08;ctrlshiftb&#xff09;”時無反應&#xff0c;Sublime左下角提示”No B…

linux 火鍋平臺,“定制版火鍋”來襲,持續創新才能永葆活力

原標題&#xff1a;“定制版火鍋”來襲&#xff0c;持續創新才能永葆活力5月1日&#xff0c;重慶涪陵紅酒小鎮的一家轉轉火鍋店&#xff0c;推出“五一”定制版火鍋免費請游客品嘗。廣西的螺螄粉、貴州的折耳根、湖南臭豆腐、福建烏龍茶、重慶榨菜、河南胡辣湯、陜西老陳醋、海…

internetreadfile讀取數據長度為0_YOLOV3的TensorFlow2.0實現,支持在自己的數據集上訓練...

GitHub鏈接&#xff1a;calmisential/YOLOv3_TensorFlow2?github.com我主要參考了yolov3的一個keras實現版本&#xff1a;qqwweee/keras-yolo3?github.com目前支持在PASCAL VOC 2012數據集上訓練和自定義數據集上訓練&#xff0c;具體的訓練過程可參考項目倉庫中的README文檔…

c語言用鏈表對學生成績排序,學生成績排序和平均分計算利用c語言鏈表的創建插入刪除.doc...

#define NULL 0#define LEN sizeof(struct student)struct student{long num;float score;struct student *next;};int n;struct student *creat(void)//創建鏈表{struct student *head;struct student *p1,*p2;n0;p1p2(struct student*)malloc(LEN);scanf("%ld,%f",…