P4445 最長迴文串

P4445 最長迴文串

題目描述

順序和逆序讀起來完全一樣的串叫做迴文串。比如acbcaacbca是迴文串,而abcabc不是(abc的順序爲abcabc,逆序爲cbacba,不相同)。

輸入長度爲nn的串SS,求SS的最長雙迴文子串TT,即可將TT分爲兩部分XXYYX,Y1X,Y1(|X|,|Y|≥1∣X∣,∣Y∣≥1)XXYY都是迴文串。

題目解答

將串ss進行預處理增加’$‘和’#'字符得到nsns串以便使用Manacher算法.

使用Manacher算法求以每個位置爲中心的最長迴文串長度數組npnp.

算法一.

根據數組pp,處理出數組lft,rgtlft,rgt

lft[i]lft[i]表示nsns中以ii爲右端點的最長迴文串的中心位置.

rgt[i]rgt[i]表示nsns中以ii爲左端點的最長迴文串的之心位置.

ps:由於nsns字符串中包含了#\#字符,因此迴文串中心到一端的距離,就可以實際表示一個迴文串的長度.

由於nsns串特殊的奇偶性,我們枚舉ii,計算rgt[i+1]lft[i]{rgt[i+1]-lft[i]}的最大值就是答案.

問題變成了如何求lft,rgtlft,rgt數組.

lftlft爲例,rgtrgt求法類似:

枚舉迴文串中心的位置ii,則lft[i,i+np[i])lft[i,i+np[i])區間內未設置值得位置全都設置成爲ii.

實現代碼

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>

const int N = 100007;
char s[N],ns[N<<1];int np[N<<1];

#define pr(x) std::cout << #x << ":" << x << std::endl

std::vector<int> vec[N<<1];
int vis[N<<1];

int lft[N<<1],rgt[N<<1];

int Manacher() {
    int len = 0,mx = 0,id;
    ns[len++] = '$';
    ns[len++] = '#';
    for(int i = 0;s[i];++i)
        ns[len++] = s[i],ns[len++] = '#';
    for(int i = 1;i < len;++i) {
        np[i] = mx > i ? std::min(np[2*id-i],mx-i):1;
        while(ns[i-np[i]] == ns[i+np[i]]) 
            ++np[i];
        if(np[i]+i > mx) 
            mx = np[i]+i,id = i;
    }
    int p = 0;
    for(int i = 0;i < len;++i) {
        for(;p < i + np[i];++p) {
            lft[p] = i;
        }
    }	
    p = len;
    for(int i = len-1;i > 0;--i) {
        for(;p > i - np[i];--p) {
            rgt[p] = i;
        }
    }
    int ans = 0;
    for(int i = 1;i < len;i++) {
        ans = std::max(ans,rgt[i+1] - lft[i]);
    }	
    return ans;
}

int main() {
    std::cin >> s;
    std::cout << Manacher() << std::endl;
    return 0;
}

算法二.

計算數組lft,rgtlft,rgt

lft[i]lft[i]表示以ii爲右端點的最長迴文子串在ss中的實際長度.

rgt[i]rgt[i]表示以ii爲左端點的最長迴文子串在ss中的實際長度.

那麼答案就是枚舉ii爲偶數,ans=max(lft[i]+rgt[i+2])ans = max(lft[i] + rgt[i+2])

lftlft爲例,rgtrgt類似:

從小到大枚舉ii,並用一個優先隊列維護當前最小的迴文串中心點ii,設定在i+np[i]i+np[i]位置將優先隊列中的ii設爲失效.

每次從優先隊列中取出的第一個有效的數即是lft[i]lft[i].

這種算法的思路與算法一本質上是一樣的,只不過枚舉量不同,算法一枚舉的是lft[i]lft[i],然後利用單調性將時間複雜度降低到了O(n)O(n)

算法二枚舉的是ii,需要用數據結構來維護,時間複雜度是O(nlogn)O(nlogn)

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>

const int N = 100007;
char s[N],ns[N<<1];int np[N<<1];

#define pr(x) std::cout << #x << ":" << x << std::endl

std::vector<int> vec[N<<1];
int vis[N<<1];

int lft[N<<1],rgt[N<<1];

int Manacher() {
    int len = 0,mx = 0,id;
    ns[len++] = '$';
    ns[len++] = '#';
    for(int i = 0;s[i];++i)
        ns[len++] = s[i],ns[len++] = '#';
    for(int i = 1;i < len;++i) {
        np[i] = mx > i ? std::min(np[2*id-i],mx-i):1;
        while(ns[i-np[i]] == ns[i+np[i]]) 
            ++np[i];
        if(np[i]+i > mx) 
            mx = np[i]+i,id = i;
    }
        
    for(int i = 0;i < (N << 1);++i) vec[i].clear();
    memset(vis,0,sizeof(vis));	
    std::priority_queue<int,std::vector<int>,std::greater<int> > lQ;
    for(int i = 1;i < len;++i) {
        for(auto u : vec[i]) vis[u] = 1;
        while(!lQ.empty() && vis[lQ.top()]) lQ.pop();
        lft[i] = 1;
        if(lQ.empty()) {
            lQ.push(i);
            vec[i + np[i]].push_back(i);

            continue;
        }
        else {
            lft[i] = std::max(lft[i],(i-lQ.top()+1)/2*2+(lQ.top()%2==0));
        }
        lQ.push(i);
        vec[i + np[i]].push_back(i);
    }

    for(int i = 0;i < (N << 1);++i) vec[i].clear();
    memset(vis,0,sizeof(vis));	
    std::priority_queue<int> gQ;
    for(int i = len-1;i;--i) {
        for(auto u : vec[i]) vis[u] = 1;
        while(!gQ.empty() && vis[gQ.top()]) gQ.pop();
        rgt[i] = 1;
        if(gQ.empty()) {
            gQ.push(i);
            vec[i - np[i]].push_back(i);
            continue;
        }
        else {
            rgt[i] = std::max(rgt[i],(gQ.top()-i+1)/2*2+(gQ.top()%2==0));
        }
        gQ.push(i);
        vec[i - np[i]].push_back(i);
    }
    int ans = 0;
    for(int i = 1;i+2 <= len;++i) 
        if(i%2==0)
            ans = std::max(ans,lft[i]+rgt[i+2]);
    
    return ans;
}

int main() {
    std::cin >> s;
    std::cout << Manacher() << std::endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章