日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符串处理 —— 回文串相关 —— Manacher 算法

發布時間:2025/3/17 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符串处理 —— 回文串相关 —— Manacher 算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【概述】

Manacher 算法又稱馬拉車算法,用于求最長回文子串。

對于最長回文子串傳統的求法的求法是以每個字符為中心,向兩邊尋找回文子串,在遍歷完整個數組后即可得到最長回文子串,其時間復雜度為 O(n^2)

而馬拉車算法,將求最長回文子串的時間復雜度提升到了線性,其時間復雜度只有 O(n)

【算法流程】

1.預處理

由于字符串的長度分為奇偶兩種,因此對于初始的字符串,在每一個字符的左右都加上一個未在串中出現過的字符,得到一個新的字符數組 newStr[]。

比如:

  • 奇數字符串:bob --> #b#o#b#
  • 偶數字符串:noon --> #n#o#o#n#

這樣一來,無論原字符串是奇數個字符還是偶數個字符,處理后的字符串字符的個數都是奇數個,這樣就無需分情況討論了。

2.設置輔助數組

接下來,還需要定義一個輔助數組 p[],其中 p[i] 表示以字符 newStr[i] 為中心的最長回文半徑,若 p[i]=1,則該回文子串就是 newStr[i] 本身。

例如:以字符串 abbahopxp?為例

i012345678910111213141516171819
newStr[i]$#a#b#b#a#h#o#p#x#p#
p[i]?1212521212121214121

可以看出,p[i]-1 即為原字符串中最長回文串的長度。

3.求解輔助數組

首先新增兩個變量 mx 與 id,其中 mx 代表以 id 為中心的最長回文串的右邊界,即:mx=id+p[id]

假設求以 i 為中心的最長回文半徑,即求解 p[i],當 i<mx 時,那么有 p[i] = min(p[2*id-i], mx-i),否則 p[i]=1,其中 2*id-i 為圖中的 j 點,即 p[j] 是以 j 為中心的最長回文半徑

【復雜度分析】

基于回文串的性質,輔助數組 p[i] 的值基于以下三種情況得出:

1)j 的回文串有一部分在 id 的回文串之外

如上圖,黑線為 id 的回文串,i 與 j 關于 id 對稱,紅線為 j 的回文,紫線為 i 的回文,虛線為以 id 為中心的回文右邊界 mx,那么根據 p[i]=mx-i,即紫線的部分,那么可以得出,p[i] 無法得到更大的值

如上圖,假設右側新增的紫色 d 部分是 p[i] 可增加的部分,那么根據回文的性質,也即是說 id 的回文 = 黑線 + a + d,矛盾,假設不成立,因此 p[i] 無法再增加,得到一個更大的值

2)j 回文串全部在 i 內部

如上圖,此時 p[i]=p[j],那么可以得出,p[i] 無法得到更大的值

如上圖,假設右側的紅線 d 是 p[i] 可增加的部分,那么根據回文的性質,j 的回文 = j 之前的回文 + a + b,矛盾,假設不成立,因此 p[i] 無法再增加,得到一個更大的值

3)i?的回文串右端與 id 的回文串右端重合

如上圖,此時 p[i]=p[j],或 p[i]=mx-i,此時?p[i] 可以繼續增加,那么有:

while(newStr[i-p[i]]==newStr[i+p[i]]) p[i]++;

根據(1)(2)(3),容易推出 Manacher 算法的最壞情況,即為字符串內全是相同字符的時候,平均訪問每個字符 5 次,同理,也容易推出 Manacher 算法的最佳情況,即為字符串內字符各不相同的時候,平均每個字符訪問 4 次。

綜上,Manacher算法的時間復雜度為 O(n)。

【實現】

char str[N];//原字符串 char newStr[N*2];//預處理后的字符串 int p[N*2];//輔助數組 int init(){//對原字符進行預處理newStr[0]='$';newStr[1]='#';int j=2;int len=strlen(str);for (int i=0;i<len;i++){newStr[j++]=str[i];newStr[j++]='#';}newStr[j] ='\0'; //字符串結束標記return j;//返回newStr的長度 }int manacher(){int len=init();//取得新字符串長度并完成字符串的預處理int res=-1;//最長回文長度int id;int mx=0;for(int i=1;i<len;i++){int j=2*id-i;//與i相對稱的位置if(i<mx)p[i]=min(p[j], mx-i);elsep[i]=1;//由于左有'$',右有'\0',不需邊界判斷while(newStr[i-p[i]] == newStr[i+p[i]])//p[i]的擴大p[i]++;if(mx<i+p[i]){//由于希望mx盡可能的遠,因此要不斷進行比較更新id=i;mx=i+p[i];}res=max(res,p[i]-1);}return res; }int main(){while (printf("請輸入字符串:\n")){scanf("%s",str);printf("最長回文長度為 %d\n", manacher());}return 0; }

?

總結

以上是生活随笔為你收集整理的字符串处理 —— 回文串相关 —— Manacher 算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。