[hihoCoder] #1032 : 最長回文子串


時間限制:1000ms
單點時限:1000ms
內存限制:64MB

描述

   小Hi和小Ho是一對好朋友,出生在信息化社會的他們對編程產生了莫大的興趣,他們約定好互相幫助,在編程的學習道路上一同前進。

   這一天,他們遇到了一連串的字符串,於是小Hi就向小Ho提出了那個經典的問題:“小Ho,你能不能分別在這些字符串中找到它們每一個的最長回文子串呢?”

   小Ho奇怪的問道:“什么叫做最長回文子串呢?”

   小Hi回答道:“一個字符串中連續的一段就是這個字符串的子串,而回文串指的是12421這種從前往后讀和從后往前讀一模一樣的字符串,所以最長回文子串的意思就是這個字符串中最長的身為回文串的子串啦~”

   小Ho道:“原來如此!那么我該怎么得到這些字符串呢?我又應該怎么告訴你我所計算出的最長回文子串呢?

   小Hi笑着說道:“這個很容易啦,你只需要寫一個程序,先從標准輸入讀取一個整數N(N<=30),代表我給你的字符串的個數,然后接下來的就是我要給你的那N個字符串(字符串長度<=10^6)啦。而你要告訴我你的答案的話,只要將你計算出的最長回文子串的長度按照我給你的順序依次輸出到標准輸出就可以了!你看這就是一個例子。”

提示一  提示二  提示三  提示四
樣例輸入
3
abababa
aaaabaa
acacdas
樣例輸出
7
5
3

傳說中的Manacher算法,算法的核心就在這一句上了:p[i] = min(p[2*id-i], p[id] + id - i);

原文地址:
http://zhuhongcheng.wordpress.com/2009/08/02/a-simple-linear-time-algorithm-for-finding-longest-palindrome-sub-string/
    其實原文說得是比較清楚的,只是英文的,我這里寫一份中文的吧。
    首先:大家都知道什么叫回文串吧,這個算法要解決的就是一個字符串中最長的回文子串有多長。這個算法可以在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開始的)

if( mx > i)
    p[i]= MIN( p[2*id-i], mx-i);
 
就是當前面比較的最遠長度mx>i的時候,P[i]有一個最小值。這個算法的核心思想就在這里,為什么P數組滿足這樣一個性質呢?
   (下面的部分為圖片形式)


 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int N;
 5 string s;
 6 
 7 void solve() {
 8     string s1;
 9     s1.resize(2 * s.size() + 2);
10     s1[0] = '$';
11     s1[1] = '#';
12     for (int i = 0; i < s.size(); ++i) {
13         s1[(i + 1) << 1] = s[i];
14         s1[((i + 1) << 1) + 1] = '#';
15     }
16     vector<int> p(s1.size(), 0);
17     int res = 0;
18     for (int id = 0, i = 1; i < s1.size(); ++i) {
19         if (p[id] + id > i) p[i] = min(p[2 * id - i], p[id] + id - i);
20         else p[i] = 1;
21         while (s1[i + p[i]] == s1[i - p[i]]) ++p[i];
22         if (i + p[i] > id + p[id]) id = i;
23         res = max(res, p[i]);
24     }
25     cout << res - 1 << endl;
26 }
27 
28 int main() {
29     cin >> N;
30     while (N--) {
31         cin >> s;
32         solve();
33     }
34     return 0;
35 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM