hihoCoder第一週(最長迴文子串)

  • 求一個字符串的最長迴文子串最容易想到的第一個方法就是枚舉每個子串,然後判斷它是不是迴文串,枚舉子串時我們不需要創建新串,只需要比較字符串的第一個字符和最後一個字符是否相同,第二個字符和倒數第二個字符是否相同,以此類推。
  • 如果一個字符串的[3, 7]這一段已經不是迴文子串了,[2, 8]這一段也不可能是迴文子串,這時第一種方法很多計算就白費了。我們在枚舉子串的時候換一種方式來進行枚舉,不是枚舉它的起止位置而是嘗試枚舉子串的中心位置,然後再從小到大依次枚舉這個子串的長度,一旦發現已經不是一個迴文串了就繼續嘗試下一個中心位置。
  • 但是一個全是a的字符串,上一種枚舉方法似乎也沒有多大用處。現在考慮如果以第5個字符爲中心的最長迴文子串的長度是5的話,這就告訴了我們[3, 7]這一段是一個迴文子串。假設這時候你想要計算以第6個字符爲中心的最長迴文子串的長度,有什麼已知的信息呢?
    • 首先第6個字符和第4個字符是一樣的,第7個字符和第3個字符是一樣的,而第5個字符本身就肯定和第5個字符一樣,那麼如果[3, 5]這一段是迴文子串的話,那麼[5, 7]這一段肯定也是迴文子串。也就是說,如果令f[i] 表示以第i個字符爲中心的最長迴文子串的長度,我們就會有f[i] >= f[i–2]
    • 還要考慮到f[i – 1]的值,如果f[i – 1]太小就沒有意義了,應該是f(i)≥min⁡{f(i-2), f(i-1)-2}爲什麼沒有意義呢?因爲我們要得到f[i] >= f[i–2]必須保證i-2到i這一段是迴文串,也就是說f[i-1]至少是3,所以我們需要從f[i-2]和f[i-1]-2中選一個小的。
  • 我們再考慮f(5) = 1,但是f(4) = 7, f(2) = 3,理論上來說,我可以通過這些信息知道f(6)>=3,但是由於f(5)=1所以我只能計算出來f(6)>=-1。我們不應該是通過f(i – 1)來輔助計算,而是通過使得右邊界(j + f(j) / 2)最大的那個j來輔助計算,所以公式將變成f(i) ≥ min{f(2*j-i) , f(j) -2*(i-j)}這種形式。2*j-i是i以j爲中心點對稱的那個點,f(j) -2*(i-j)是防止以j爲中心迴文串不包括i
    • 我們只要在之前枚舉中心位置那種方法的基礎上,統計使得迴文串右邊界(j + f(j) / 2)最大的那個j,然後再計算每一個i的時候,都可以通過f(i)≥min⁡{f(2*j-i), f(j)-2*(i-j)}這個公式來知道f(i)的一個最小值,這樣即使是在我們所提到的那種最壞情況下,也可以節省掉很多不必要的計算。
  • 最後考慮一個問題,我們上面考慮的解決方法只能處理長度爲奇數的迴文子串,那麼如何解決長度爲偶數的迴文子串呢?我們在原來字符串的基礎上,在任意兩個相鄰的字符間都插入一個特殊字符,這樣無論是原來字符串中長度爲奇數的迴文子串還是長度爲偶數的迴文子串,在新的字符串中都有一個長度爲奇數的迴文子串與之進行對應。
#include <bits/stdc++.h>
using namespace std;

const int N = 1 << 23;
int rad[N];
char s[N],str[N];

int main()
{
    int t;
    scanf("%d",&t);
    while(t --) {
        scanf("%s",s);
        str[0] = '$';
        str[1] = '#';
        int n = 2;
        for(int i = 0; s[i]; i ++) {//插入字符
            str[n ++] = s[i];
            str[n ++] = '#';
        }
        int ma = 0, re;//ma保存當前最大回文子串的最後一個下標,re保存當前迴文子串的中心
        for(int i = 1; i < n; i ++) {
            if(ma > i) rad[i] = min(ma - i,rad[2 * re - i]);
            else rad[i] = 1;
            while(str[i - rad[i]] == str[i + rad[i]]) rad[i] ++;
            if(rad[i] + i > ma) {
                ma = rad[i] + i;
                re = i;
            }
        }
        int ans = 0;
        for(int i = 1;i < n;i ++)
            ans = max(ans, rad[i] - 1);
        printf("%d\n",ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章