Manacher算法--O(n)迴文子串算法

這裏,我介紹一下On)迴文串處理的一種方法。Manacher算法.
    首先:大家都知道什麼叫回文串吧,這個算法要解決的就是一個字符串中最長的迴文子串有多長。這個算法可以在On)的時間複雜度內既線性時間複雜度的情況下,求出以每個字符爲中心的最長迴文有多長,
    這個算法有一個很巧妙的地方,它把奇數的迴文串和偶數的迴文串統一起來考慮了。這一點一直是在做迴文串問題中時比較煩的地方。這個算法還有一個很好的地方就是充分利用了字符匹配的特殊性,避免了大量不必要的重複匹配。
    算法大致過程是這樣。先在每兩個相鄰字符中間插入一個分隔符,當然這個分隔符要在原串中沒有出現過。一般可以用‘#’分隔。這樣就非常巧妙的將奇數長度迴文串與偶數長度迴文串統一起來考慮了(見下面的一個例子,迴文串長度全爲奇數了),然後用一個輔助數組P記錄以每個字符爲中心的最長迴文串的信息。Pid]記錄的是以字符strid]爲中心的最長迴文串,當以strid]爲第一個字符,這個最長迴文串向右延伸了Pid]個字符。
    原串:    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
    這裏有一個很好的性質,Pid-1就是該回文子串在原串中的長度(包括‘#’)。如果這裏不是特別清楚,可以自己拿出紙來畫一畫,自己體會體會。當然這裏可能每個人寫法不盡相同,不過我想大致思路應該是一樣的吧。
    好,我們繼續。現在的關鍵問題就在於怎麼在On)時間複雜度內求出P數組了。只要把這個P數組求出來,最長迴文子串就可以直接掃一遍得出來了。
    由於這個算法是線性從前往後掃的。那麼當我們準備求Pi]的時候,i以前的Pj]我們是已經得到了的。我們用mx記在i之前的迴文串中,延伸至最右端的位置。同時用id這個變量記下取得這個最優mx時的id值。(注:爲了防止字符比較的時候越界,我在這個加了‘#’的字符串之前還加了另一個特殊字符‘$’,故我的新串下標是從1開始的)
好,到這裏,我們可以先貼一份代碼了。

#include<vector>
#include<iostream>
using namespace std;

const int N=300010;
int n, p[N];
char s[N], str[N];

#define _min(x, y) ((x)<(y)?(x):(y))

void kp()
{
    int i;
    int mx = 0;
    int id;
    for(i=1; i<n; i++)
    {
        if( mx > i )
            p[i] = _min( p[2*id-i], p[id]+id-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;
        }
    }
}

void init()
{
	int i, j, k;
	str[0] = '$';
	str[1] = '#';
	for(i=0; i<n; i++)
	{
		str[i*2+2] = s[i];
		str[i*2+3] = '#';
	}
	n = n*2+2;
	s[n] = 0;
}

int main()
{
	int i, ans;
	while(scanf("%s", s)!=EOF)
	{
		n = strlen(s);
		init();
		kp();
		ans = 0;
		for(i=0; i<n; i++)
			if(p[i]>ans)
				ans = p[i];
		printf("%d\n", ans-1);
	}
	return 0;
}

   代碼是不是很短啊,而且相當好寫。很方便吧,還記得我上面說的這個算法避免了很多不必要的重複匹配吧。這是什麼意思呢,其實這就是一句代碼。

if( mx > i)
    p[i]=MIN( p[2*id-i], mx-i);

就是當前面比較的最遠長度mx>i的時候,Pi]有一個最小值。這個算法的核心思想就在這裏,爲什麼P數組滿足這樣一個性質呢?
   (下面的部分爲圖片形式)




    看完這個算法,你有可能會覺得這種算法在哪會用到呢?其實迴文串後綴數組也可以做。只是複雜度是On log n)的,而且一般情況下也不會刻意去卡一個log n的算法。可正好hdu就有這麼一題,你用後綴數組寫怎麼都得T(當然應該是我寫得太爛了)。不信的話大家也可以去試試這題。
        http://acm.hdu.edu.cn/showproblem.php?pid=3068
    另外,順便附一份AC代碼。

        http://acm.hust.edu.cn:8080/judge/problem/viewSource.action?id=140283

轉自:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章