好久沒有刷題了,雖然參加過acm,但是始終沒有融會貫通,沒有學個徹底。我幹啥都是半吊子,一瓶子不滿半瓶子晃蕩。
就連簡單的manacher演算法我也沒有刷過,常常為歲月蹉跎而感到後悔。
給定乙個字串s,求最長回文子串。
回文子串的回文指的是abccba這種從前往後讀和從後往前讀一樣。
子串必須連續(比如從i到j,s[i:j]),不是最長子序列(最長回文子串行怎麼求?),子串行是可以不連續的。
ans[i]表示以字元i為中心的最長回文子串的長度
now表示now+ans[now]取得最大值的那個下標
對於當前字元i,如果i處在以now為中心的回文子串裡,那麼ans[i]的求法可以參考i關於now的對稱點的回文子串長度,也就是ans[now-(i-now)].
例如:1j34now67i9,假設ans[j]=1,那麼ans[i]也等於1,因為i和j都處在以now為中心的回文子串裡面,它們是對稱的。
上面所述即為演算法關鍵,其餘情形很容易自己想到。
但是manacher演算法用到了兩個技巧
ans[i]中記錄的是以i為中心的最長回文子串,如果不作處理,這樣只能夠檢測出長度為奇數的回文子串的最大長度。所以有乙個巧妙的預處理。
給定字串abcc,擴充成#a#b#c#c#。
#a#b#a# 長度為3,以字元為中心的情況
#a#a#a#a# 長度為4,以#為中心的情況
這樣奇數偶數統一化處理。
如果在for迴圈中檢測兩個條件,那是很費事的,效率低。
如何判斷乙個條件有很多次無效的判斷?就看這個條件發揮作用,影響程式分支的次數和進行條件求值的次數。
邊界條件判斷影響分支的次數很少,但卻每次都要進行判斷。
通過加上乙個終止字元,就能夠避免邊界條件判斷。
在manacher演算法中,要求回文子串同時要防止下標越界。所以直接在開頭插入乙個$字元,這樣肯定因為失配而終止。
manacher演算法為線性複雜度,因為從前往後有乙個指標一直是單方向運動,沒有回溯。
對於陣列中的多個指標,如果都是單向運動,儘管它們運動的順序和步長不同,那也一定是線性複雜度。
#include#includeusing namespace std;
const int n = 110009;
char s[n];
char a[n * 2];
int ans[n * 2];
int now;
int main()
a[j++] = '#';
//開始演算法主體部分
now = 1;
ans[0] = ans[1] = 0;
for (int i = 2; i < j; i++)
else
else}}
//尋找答案,這部分可以直接放在求ans的過程中
int ma = 0;
for (int i = 1; i < j; i++)
printf("%d\n", ma);
}return 0;
}
動態規劃:複雜度都是o(n^2)
方法一:
a[i,j]表示s[i,j]之間最長回文子串行。則a[i,j]可以來自a[i+1,j-1],a[i-1,j],a[i,j-1].
方法二:
將s和s反過來得到的字串求最長公共子串行
hdu3068回文串Manacher演算法
題目就是求乙個串的最大回文子串的字元個數。manacher 演算法先貼乙個模板。好短啊。話說此題字尾陣列可能會超時的。發現這個模板有點問題,在此更正一下。更正後 include includeusing namespace std const int n 300010 int n,p n char ...
HDU 3068 最長回文串
用的manacher法,o n 複雜度,證明跟kmp演算法一樣說不清 基本上是參考部落格鏈結 dp法記憶體不夠,中心擴充套件法時間不夠 manacher法 include include include include include include include include include ...
HDU 3068 最長回文串
解法1 根據是奇數串還是偶數串,遍歷中點,更新最長max值 複雜度o n 2 解法2 manacher 馬拉車演算法 o n 馬拉車演算法 1.中間插入 符號,統一變為奇數串規避奇偶問題 2.使用乙個輔助p陣列,p i 表示以 i 為中心的最長回文的半徑,p i 1正好是原字串中最長回文串的長度 3...