乙個字串中連續的一段就是這個字串的子串,而回文串指的是」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 你能不能...