馬拉車算法:用於求解最長迴文子串的問題。由於它可以以O(n)的時間複雜度求解答案,所以十分的高效。算法
算法思想:馬拉車算法主要是利用已求解的迴文子串內部具備對稱性,藉由此來進行加速處理。數據結構
舉個例子:spa
1.字符串:abbababa 最長迴文子串:5(abbababa)3d
2.字符串:abcbbabbc 最長迴文子串:7(abcbbabbc)指針
3.字符串:abccbaba 最長迴文子串:6(abccbaba)code
傳統方法是,遍歷每一個字符,以該字符爲中心向兩邊查找。時間複雜度爲O(n^2),效率不好;blog
在此基礎上能夠先對字符串哈希一下,而後用二分求解,能夠達到O(nlogn)。排序
最後最優的求解此類問題的算法就是咱們的馬拉車算法了,他達到了神奇的O(n)。字符串
迴文串分爲奇迴文和偶迴文,而咱們的馬拉車算法進行了一個騷操做,將全部迴文子串都處理成了奇迴文。咱們在每一個字符的前邊和後邊都插入一個沒有出現過的字符,最後再在最前面插入一個沒有出現過的字符(防止越界),就能夠達到這個神奇的效果。get
舉個例子:
字符串:abbababa
變換以後:$#a#b#b#a#b#a#b#a#\0
迴文子串:#a#, #a#b#a#b#a#
這樣變換後的字符串中的迴文子串就都變成了奇迴文。
咱們規定以s[i]爲中心的迴文子串的半徑爲p[i],即迴文子串的長度爲2 * p[i] - 1,如上邊#a#b#a#b#a#,迴文半徑爲6,長度爲11,還能夠得出原字符串此時對應的迴文子串長度爲5,即p[i] - 1 = 6 - 1 = 5。
能夠多畫幾個例子看看,這個是根據變換後字符串的規律獲得的。
那麼,p[i]應該如何計算呢。
咱們計算的時候從左往右計算。由圖咱們可知此時p[j](j = 2 * id - i)已經計算出來了。圖中表示如今(2 * id - mx, mx)區域內爲一個迴文子串,所以咱們能夠得知p[i] = p[j] (i < mx),而後再循環檢索一下以i爲中心還能再擴大半徑不,就能夠求得p[i],話很少說,上代碼:
const int maxn = 2e6 + 7;
char s[maxn], a[maxn]; int p[maxn];
int Manacher(char s[], int len) {
int l = 0;
a[l++] = '$';
a[l++] = '#';
for (int i = 0; i < len; i++) {
a[l++] = s[i];
a[l++] = '#';
}
a[l] = 0;
int mx = 0, id = 0, maxLen = -1;
for (int i = 1; i < l - 1; i++) {
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
while (a[i + p[i]] == a[i - p[i]]) p[i]++;
if (i + p[i] > mx) mx = i + p[i], id = i;
if (p[i] > maxLen) maxLen = p[i] - 1;
}
return maxLen;
}
然咱們來看看例題:
acwing: https://www.acwing.com/problem/content/141/
字典樹:可以快速查詢當前字符串是否出現過和其餘相關問題。
算法思想:利用空間換取時間。預處理建一棵樹,把內容都記錄下來,以下:
(標橙色的節點是「目標節點「,即根節點到這個目標節點的路徑上的全部字母構成了一個單詞。)
字典樹就是一棵樹,樹邊記錄了一些字符,結點記錄了一些標記信息。
這就是字典樹的概念。結合上面說的概念,上圖所示的字典樹包括的單詞分別爲:
a abc bac bbc ca
根據字典樹的概念,咱們能夠發現:字典樹的本質是把不少字符串拆成單個字符的形式,以樹的方式存儲起來。因此咱們說字典樹維護的是」字典「。那麼根據這個最基本的性質,咱們能夠由此延伸出字典樹的不少妙用。簡單總結起來大致以下:
一、維護字符串集合(即字典)。
二、向字符串集合中插入字符串(即建樹)。
三、查詢字符串集合中是否有某個字符串(即查詢)。
四、統計字符串在集合中出現的個數(即統計)。
五、將字符串集合按字典序排序(即字典序排序)。
六、求集合內兩個字符串的LCP(Longest Common Prefix,最長公共前綴)(即求最長公共前綴)。
字典樹的兩種基本操做爲建樹和查詢。
存儲數據結構:
const int maxn = 2e5 + 7;
int trie[maxn][26], col[maxn], tot = 1;
char s[maxn];
其中,trie[i][j]表示一個指向子節點的指針,col[i]表示一個終止標記,tot記錄現有的結點個數
插入操做:
void insert(char* s) { int len = strlen(s), p = 1; for (int i = 0; i < len; i++) { int k = s[i] - 'a'; if (!trie[p][k]) trie[p][k] = ++tot; p = trie[p][k]; } col[p] = 1; }
查詢操做:
bool search(char* s) { int len = strlen(s), p = 1; for (int i = 0; i < len; i++) { int k = s[i] - 'a'; if (!trie[p][k]) return false; p = trie[p][k]; } return col[p] == 1; }
來看看例題: https://www.acwing.com/problem/content/144/ https://www.acwing.com/problem/content/163/