hiho 1 最長回文子串

2021-07-06 02:29:12 字數 3128 閱讀 1530

乙個字串中連續的一段就是這個字串的子串,而回文串指的是」12421」這種從前往後讀和從後往前讀一模一樣的字串,所以最長回文子串的意思就是這個字串中最長且為回文串的子串啦~

最笨的方法是:列舉字串的每個位置,求出以當前位置為中心的回文子串的長度。

這個方法有兩個問題:

1.只考慮了所有的長度為奇數的回文子串。

2.每次列舉的複雜度為o(n), 總體複雜度為o(n2

n 2)

處理長度為偶數的子串

其實只要將所有的字元用特殊字元隔開,就可以用上面求奇數的演算法處理偶數長度的情況了。

例如原始字串為 「abaaba」,

預處理後為」#a#b#a#a#b#a#」

按照上面方法當列舉到第7個字元#時可以得到最長的回文子串(#a#b#a#a#b#a#),然後去掉所有的# 就是答案(abaaba)。

o(n) 解法

1 中已經將答案為偶數的情況轉換為奇數的求解,下面只考慮答案為奇數的情況。

笨方法中其實是有重複處理的,考慮下面的例子:序號1

2345

678s(i)ab

abab

acf(i)01

232?

第三行的f(i)表示以當前位置 i 為中心的最長回文子串邊界到中心 i 的距離,例如i = 4時最長回文子串為 abababa, 右邊界為s(7) = 『a』, 所以f(4) = 7-4 = 3。

現在要求f(6) = ?,笨方法求解時就要比較s(5)和s(7)是否相等,但是我們可以發現在求4號的時候已經讀入了 s(5)和s(7),也就是說s(5)和s(7)資訊已經包含在了之前的 f(i) 中,如果我們能從之前的f(i)中判斷出 s(5)=s(7),那麼就可得出f(6) >= 1 的結論。

下面就來看看其中隱藏的關係:

由f(4) = 3可知

s(7) = s(1),s(5) = s(3)

要判斷s(7) ? s(5) 等價於判斷 s(1) ? s(3)

而f(2) =1,說明

s(1) = s(3)

所以 s(7) = s(5) 因此f(6)

≥ ≥

1 我們來總結一下上面的方法,首先我們選擇了乙個輔助點 j ,這個輔助點的選擇條件是

使得f(j) + j 最大的j , 這意味著此j 的回文串右邊界最大,也就是說在求f(j) 時讀取了更靠右邊的字元,這樣 f(j)包含了更多靠後的字元資訊。

輔助點j 的使用方法如下圖所示

圖中綠色表示輔助點j 的最長回文串, i_mirror 是指以j為中心i的對稱點

對於關於i對稱的兩個點 a 和 b 要判斷 s(a) ? s(b) 我們可以將a b 以j 為中心找到其對應的對稱點a』b』

由於a和a』 在j 的回文串中 且關於j 對稱

因此 s(a) = s(a』) 同理 s(b) = s(b』)

現在轉換為要判斷s(a』) ? s(b』)

a』 和 b』 是關於 i_mirror 對稱的兩個點(想想為什麼),如果a』 和 b』 在i_mirror 的回文串中,就有

s(a』) = s(b』)

而這個判斷等價於

f(i_mirror)

≥ ≥

a』 - i_mirror = i - a = b - i

=> b-i

≤ ≤

f(i_mirror) …………………….(1)

同時要保證 b和 b』在j 的最大回文子串中

f(j) + j

≥ ≥

b => f(i)+j -i

≥ ≥

b-i

=> b-i

≤ ≤

f(i) +j -i ……………….. (2)

由(1), (2)兩式可得:

b -i

≤ ≤

min(f(i_mirror), f(j) + j - i)

取最大的b-i作為f(i) 的初始值

f(i) = min(f(i_mirror), f(j) + j - i) …………….(3)

這樣就得到了乙個較大的f(i) 然後再向兩邊擴充套件。

最後上**

實現上的小技巧:

1. 在字串的兩端分別加上^ 和 $ 這樣就不用考慮邊界情況了。

2. 在加入# 後,求出的f() 值恰好為回文子串的長度。

3. 注意公式3 中f(i)可能< 0。

#include

#include

#include

#include

enum;

int n;

char s[maxn], str[maxn];

int f[maxn];

#define oj

int main()

s[t++] = '$';

s[t++] = '\0';

int ans = 0;

f[1] = 0;

int j = 1;

for (int i=2; i < t-1; i++)

ans = std::max(ans, f[i]);

}printf("%d\n", ans);

}return

0;}

複雜度分析:

首先如果公式3中求得的f(i_mirror) != f(j) + j - i 那麼while(s[i+f[i]] == s[i-f[i]]) f[i]++; 將只執行一次(想想為什麼)

如果f(i_mirror) == f(j) + j - i 那麼 在i 處會繼續擴充套件,while(s[i+f[i]] == s[i-f[i]]) f[i]++;將從

i+ f[i] = i+ f[j] + j -i = f[j] + j 處開始, 而f[j] + j 就是最當前讀入的最右邊字元,直到不相等為止。迴圈開始時f[i] + i = f[j] + j , 迴圈結束後有 f[i] + i

≥ ≥

f[j] + j; 此時i成為下乙個j。下次又從當前位置開始比較。所以比較的次數就是max(f(j) + j)= o(n)。

總體複雜度為o(n)。

hiho一下第1周 最長回文子串

用 馬拉車 演算法,規範 若原字串為 asdfasdfasdf 標記第乙個字元為 最後乙個字元為 然後中間再加工成 a s d f a s d f a s d f 最後整一串為 a s d f a s d f a s d f 處理完後跑一遍馬拉車 include include include in...

最長回文子串 最長回文子串行

1.最長回文子串行 可以不連續 include include include include using namespace std 遞迴方法,求解最長回文子串行 intlps char str,int i,int j intmain include include include using n...

hiho 第一周 最長回文子串

如題 時間限制 1000ms 單點時限 1000ms 記憶體限制 64mb 小hi和小ho是一對好朋友,出生在資訊化社會的他們對程式設計產生了莫大的興趣,他們約定好互相幫助,在程式設計的學習道路上一同前進。這一天,他們遇到了一連串的字串,於是小hi 就向小ho 提出了那個經典的問題 小ho 你能不能...