題目大意:如題
思路:manacher演算法,第一次沒聽懂..這次終於弄懂了..跟擴充KMP很像....
先把在部落格上看到的思想貼出來:
其實原文說得是比較清楚的,只是英文的,我這裡寫一份中文的吧。
首先:大家都知道什麼叫迴文串吧,這個演算法要解決的就是一個字串中最長的迴文子串有多長。這個演算法可以在O(n)的時間複雜度內既線性時間複雜度的情況下,求出以每個字元為中心的最長迴文有多長,
這個演算法有一個很巧妙的地方,它把奇數的迴文串和偶數的迴文串統一起來考慮了。這一點一直是在做迴文串問題中時比較煩的地方。這個演算法還有一個很好的地方就是充分利用了字元匹配的特殊性,避免了大量不必要的重複匹配。
演算法大致過程是這樣。先在每兩個相鄰字元中間插入一個分隔字元,當然這個分隔字元要在原串中沒有出現過。一般可以用‘#’分隔。這樣就非常巧妙的將奇數長度迴文串與偶數長度迴文串統一起來考慮了(見下面的一個例子,迴文串長度全為奇數了),然後用一個輔助數組P記錄以每個字元為中心的最長迴文串的資訊。P[id]記錄的是以字元str[id]為中心的最長迴文串,當以str[id]為第一個字元,這個最長迴文串向右延伸了P[id]個字元。
原串: w aa bwsw f d
新串: # w# a # a # b# w # s # w # f # d #
輔助數組P: 1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1
這裡有一個很好的性質,P[id]-1就是該迴文子串在原串中的長度(包括‘#’)。如果這裡不是特別清楚,可以自己拿出紙來畫一畫,自己體會體會。當然這裡可能每個人寫法不盡相同,不過我想大致思路應該是一樣的吧。
好,我們繼續。現在的關鍵問題就在於怎麼在O(n)時間複雜度內求出P數組了。只要把這個P數組求出來,最長迴文子串就可以直接掃一遍得出來了。
由於這個演算法是線性從前往後掃的。那麼當我們準備求P[i]的時候,i以前的P[j]我們是已經得到了的。我們用mx記在i之前的迴文串中,延伸至最右端的位置。同時用id這個變數記下取得這個最優mx時的id值。(註:為了防止字元比較的時候越界,我在這個加了‘#’的字串之前還加了另一個特殊字元‘$’,故我的新串下標是從1開始的)
好,到這裡,我們可以先貼一份代碼了。
|
複製代碼
- void pk()
{
int i;
int mx = 0;
int id;
for(i=1; i<n; i++)
{
if( mx > i )
p[i] = MIN( p[2*id-i], mx-i );
else
p[i] = 1;
for(; str[i+p[i]] == str[i-p[i]]; p[i]++)
;
if( p[i] + i > mx )
{
mx = p[i] + i;
id = i;
}
}
}
|
代碼是不是很短啊,而且相當好寫。很方便吧,還記得我上面說的這個演算法避免了很多不必要的重複匹配吧。這是什麼意思呢,其實這就是一句代碼。
if(
mx > i) p[i]=
MIN(
p[2*id-i],
mx-i);
就是當前面比較的最遠長度mx>i的時候,P[i]有一個最小值。這個演算法的核心思想就在這裡,為什麼P數組滿足這樣一個性質呢? (下面的部分為圖片形式)
其實核心的一句話就在於迴文翻轉了還是迴文這一句.
圖上也是在詮釋這一句,所以,利用前面已經匹配過的最大的迴文串,就是儘可能利用訪問過的資源
圖1中,以如果i大於mx的話,那麼就完全沒有前面的資訊可以用,只好乖乖的一個一個左右匹配,
但是如果i<mx的話,那麼就說明前面可以有相應的資源可以利用.因為以id的左右的迴文肯定包括i關於id對稱的j點處的一部分或者全部迴文.
所以如果是包括全部的話就是圖1的情況
如果只是包含部分的情況那麼就是圖2.
那麼由圖2很清晰可以看到那個迴文更短些,即mx-i
AC program:
#include <vector>#include <list>#include <map>#include <set>#include <queue>#include <deque>#include <stack>#include <bitset>#include <algorithm>#include <functional>#include <numeric>#include <utility>#include <sstream>#include <iostream>#include <iomanip>#include <cstdio>#include <cmath>#include <cstdlib>#include <cstring>#include <ctime>#include <queue>#include <cassert>typedef long long ll;#defineclr(a)memset((a),0,sizeof (a))#definerep(i,a,b)for(int i=(a);i<(int)(b);i++)#defineper(i,a,b)for(int i=((a)-1);i>=(int)(b);i--)#defineinf(0x7fffffff)#defineeps1e-6#defineMAXN#define MODN(1000000007)using namespace std;char mm[1000005]; char initmm[1000005];int p[1000005];int init(){ initmm[0]='$'; int kg=1; char t='#'; int len=strlen(mm); //著實有點要跪了,為什麼rep(i,0,strlen(mm))會逾時.暈死 rep(i,0,len){ initmm[kg++]='#'; initmm[kg++]=mm[i]; } initmm[kg++]='#'; initmm[kg]=0; return kg;}int manacher(int n){ int mx=0; int id; int maximum=-1; rep(i,0,n){ if(mx>i){ p[i]=min(p[2*id-i],mx-i); } else{ p[i]=1; } for(;initmm[i-p[i]]==initmm[i+p[i]];p[i]++); if(mx<i+p[i]){ mx=i+p[i]; id=i; } maximum=max(p[i],maximum); } return maximum;}int main(){ while(scanf("%s",&mm)!=EOF){ int n=init(); cout<<manacher(n)-1<<endl; }; //system("pause"); return 0;}