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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章