馬拉車演算法的一些個人理解

2021-10-04 04:14:12 字數 2196 閱讀 4857

在一串字串裡找出最長字串的問題,可以通過暴力求解法來解決,其時間複雜度為o(n^2),這當然不會很快。演算法的設計,就是為了讓計算機能夠更加高效地處理問題。我們希望可以降低時間複雜度,在1975一位名叫manacher的人提出了這種演算法,雖然犧牲了空間複雜度,讓時間複雜度降為o(n)

馬拉車演算法首先對我們輸入的預設字串進行一次預處理,即給所有字元的左右兩邊新增上乙個相同的符號,比如「#」。假設原始字串為"aabca",那麼預處理過後就變成了「#a#a#b#c#a#」。為什麼要這樣做?我們在尋找最長字串時,要做的事情就是以某乙個值或者兩個值為中心,向兩邊擴充套件,去對比左右兩邊的值是否匹配。這裡就會有乙個問題,取決於回文串本身是奇數還是偶數,相應的中心可能是乙個值或者兩個值。通過新增額外字元,讓所有回文串都變為奇數,這就簡化了尋找基準。不過這也相應的讓空間複雜度由o(1)變味了o(n)。

原字元(奇):aba

新增額外字元後(奇):#a#b#a#

原字元(偶):bb

新增額外字元後(奇):#b#b#

不過只是新增了「#」還不夠,為了能夠準確地區分出字串的頭部和尾部,避免搜尋時出現下標溢位的情況,需要在字串頭部和尾部分別新增乙個與「#」和原始字元不同的字元,保證這倆字元永遠也不會和其他字元組合成回文串。這裡我們給頭部新增乙個符號「^」,給尾部新增乙個符號「%」。這樣,完整的處理效果如下:

處理後字串:^#a#b#a#%

已經對原始字串做完預處理,現在可以開始找最長字串了。這裡引入幾個概念:

c——中心值的下標,要求以其為下標時,r必須最大

p[i]——(除開首尾字元)以各個字元為中心時,相應的最長半徑

r——以c為中心時,其右端最遠處的下標值。計算方式為 p[c]+c

start——最長字串(在原始字串中)起始索引。計算方式為 (c-p[c])/2

想要找到最長字串,其實就是去找到擁有最大半徑的那個中心點。另外,通過比較所有p[i]的值,可以得到擁有最長半徑的maxlen及最長半徑p[maxlen]和起始索引start,那麼就可以準確定位最長字串的位置了。

接下來就開始遍歷搜尋吧,不過別急,這裡我們可以借助回文的特性,通過一些額外的技巧讓搜尋更具效率。

這個技巧的核心理念是:如果已經發現了長回文,那麼包含在長回文中的左邊的短回文在右邊肯定也是短回文。

這裡其實利用了對稱的想法,假若我們此刻的中心點為c,迭代的指標為i,那麼右邊的i相對於中心點c在左邊應該可以找到乙個相對的點i_mirror:

i_mirror——與i相對於c對稱。計算方式為 2*c-i

考慮i與r的關係:

ii==r

無需考慮i_mirror,直接令p[i]=0開始進行擴充套件。

(無需考慮i>r,因為一旦出現更大的r,c就需要更新。這就保證i所指向的值,至少等於r)

在確定p[i]的值後,以此為基礎確認左右兩端的下標值進行擴充套件搜尋,可以有效節約搜尋時間。

#include

#include

#include

using

namespace std;

string preprocess

(string s)

string ret =

"^";

for(

int i =

0; i < n; i++

) ret +

="#%"

;return ret;

}string longestpair

(string s)

else

// 確定了p[i]的值後,開始進行中心擴充套件

while

(s[i+

1+p[i]

]== s[i-

1-p[i]])

// 判斷是否需要更新 r

if(i + p[i]

> r)

}// 找出 p 的最大值並計算相應的初始索引

int centerindex =1;

for(

int i =

2; i < n -

1; i++)}

int start =

(centerindex - p[centerindex])/

2;//起始索引值

return s.

substr

(start, p[centerindex]);

}

參考:

遞迴演算法 一些個人理解

遞迴演算法,從定義上來說,是一種直接或者間接地呼叫自身的一種演算法,這裡的自己可以是函式或者方法。遞迴演算法的思想或者說實質就是把實際的問題分解成規模較小的乙個個的子問題,然後不斷使用遞迴呼叫方法來求得問題的解。要學習和使用遞迴演算法,就要從最基本的定義上來先解讀。遞迴演算法是把問題轉化為規模縮小了...

關於KMP演算法的一些個人理解

kmp演算法其實理解起來也不難,只不過很多過於公式化的講解以及太過晦澀的說法會讓人一頭霧水 kmp演算法主要是判斷乙個字串是否是另乙個字串的字串 對於這兩個字串,在演算法描述中有固定的稱謂 我們把最長的字串稱為text,需要判定的子串稱為模式pattern 對於這個問題,其實最容易想到的就是暴力列舉...

關於KMP演算法的一些個人理解

kmp演算法其實理解起來也不難,只不過很多過於公式化的講解以及太過晦澀的說法會讓人一頭霧水 kmp演算法主要是判斷乙個字串是否是另乙個字串的字串 對於這兩個字串,在演算法描述中有固定的稱謂 我們把最長的字串稱為text,需要判定的子串稱為模式pattern 對於這個問題,其實最容易想到的就是暴力列舉...