T-SQL——關於SQL開啟Excel檔案
Able was I ere I saw Elba. ----Napoléon Bonaparte(拿破崙)
一、迴文串&迴文子串
這個很好理解。
如果一個字串正著讀和反著讀是一樣的,那它就是迴文串。 eg. abba ;
如果一個字串 S 的子串 SS 為迴文串,那麼 SS 即為 S 的迴文子串;若 SSS 為 S 的迴文子串中最長的一個,那麼我們稱 SSS 為 S 的最長迴文子串。
二、Manacher演算法
如何找到一個字串的最長迴文子串呢?
我們很容易想到一個 O(n2) 的方法,即:從每個字元開始向兩邊爆搜。但顯然,這個方法效率太低下了。
如何快速求出答案,這就是 Manacher演算法的事了。
Ⅰ. 奇迴文&偶迴文
字面意思,不再贅述。
如果同時存在奇迴文和偶迴文,那麼處理起來會比較的繁瑣,下面就是Manacher 一個很巧妙的方法了:在字串收尾,即各字元間插入一個特殊的字元(指沒有出現過的字元),例如:
aba ----> #a#b#a#
abba ----> #a#b#b#a#
這樣,所有的迴文串就都成奇迴文了。
inline int Pre() { S[0]='@',S[1]='#'; int j(1); for(register int i=0,len=IP.size();i<len;++i) S[++j]=IP[i],S[++j]='預處理#'; S[++j]='\0';return j; }
Ⅱ. 最長迴文半徑
令一個迴文串中最左或最右位置的字元與其對稱軸的距離稱為 " 迴文半徑 ",令 R [ i ] 表示字元 i 的最長迴文半徑。
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
S | @ | a | # | b | # | b | # | a | # | b | $ |
R[i] | 邊界 | 1 | 1 | 2 | 4 | 2 | 1 | 4 | 1 | 2 | 邊界 |
顯然,R [ i ] - 1 即為以 i 為中心的最長迴文子串的長度。
那麼,我們要求的最長迴文子串,就成了 max { R [ i ] - 1 } 。
如何快速求出 R [ ] ???
Ⅲ.Manacher
從左往右依次討論。
設 Max 為已討論的子串中能達到的右端最大位置,P 為提供當前 Max 的字元位置。如下表:
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Max的對稱點 | P | Max |
接下來討論的位置 i 可以分為兩種情況:小於等於 Max ,大於 Max:
1. 小於等於 Max :
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Max的對稱點 | j(i的對稱點) | P | i | Max |
由於 i 和 j 對稱 ,所以求出 i 的對稱點 j ,簡直是 "輕易而舉" 。
不難發現現在有可以分為兩種情況討論:
( 1 ) .以 j 為對稱軸的迴文串比較短:那麼可以直接令 R [ i ] = R [ j ] ;
( 2 ) .以 j 為對稱軸的迴文串比較長:此時,我們只能確定不超過 Max 的部分的情況,對於 Max 以外的,我們需要以 i 為中心開始往兩側拓展,直到兩側不同,同時更新 p 和 Max。
2. 大於 Max : 這種情況很好處理,直接拓展就可以了(當然,要同時更新 p 和 Max)
Ⅳ. 程式碼
#pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> using namespace std; char S[50000005]; int m,R[50000005]; string IP; inline int Pre() { S[0]='@',S[1]='#'; int j(1); for(register int i=0,len=IP.size();i<len;++i) S[++j]=IP[i],S[++j]='#'; S[++j]='\0';return j; } inline int Manacher() { m=Pre(); int RET(-1),x(0),Max(0); for(register int i=1;i<m;++i) { if(i<Max) R[i]=min(R[2*x-i],Max-i+1); else R[i]=1; while(S[i-R[i]]==S[i+R[i]]) ++R[i]; if(Max<i+R[i]-1) x=i,Max=i+R[i]-1; RET=max(RET,R[i]-1); } return RET; } int main() { ios::sync_with_stdio(false); cin>>IP,cout<<Manacher(); return 0; }Manacher
Ⅴ. 時間複雜度
由於Max是不斷向右拓展的,最多拓展 n 次,不難得出 馬拉車 的時間複雜度是線性的,即 O( n )。