P4445 最长回文串

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37517391/article/details/83094819

P4445 最长回文串

题目描述

顺序和逆序读起来完全一样的串叫做回文串。比如 a c b c a acbca 是回文串,而 a b c abc 不是(abc的顺序为 a b c abc ,逆序为 c b a cba ,不相同)。

输入长度为 n n 的串 S S ,求 S S 的最长双回文子串 T T ,即可将 T T 分为两部分 X X Y Y X , Y 1 X , Y 1 (|X|,|Y|≥1∣X∣,∣Y∣≥1) X X Y Y 都是回文串。

题目解答

将串 s s 进行预处理增加’$‘和’#'字符得到 n s ns 串以便使用Manacher算法.

使用Manacher算法求以每个位置为中心的最长回文串长度数组 n p np .

算法一.

根据数组 p p ,处理出数组 l f t , r g t lft,rgt

l f t [ i ] lft[i] 表示 n s ns 中以 i i 为右端点的最长回文串的中心位置.

r g t [ i ] rgt[i] 表示 n s ns 中以 i i 为左端点的最长回文串的之心位置.

ps:由于 n s ns 字符串中包含了 # \# 字符,因此回文串中心到一端的距离,就可以实际表示一个回文串的长度.

由于 n s ns 串特殊的奇偶性,我们枚举 i i ,计算 r g t [ i + 1 ] l f t [ i ] {rgt[i+1]-lft[i]} 的最大值就是答案.

问题变成了如何求 l f t , r g t lft,rgt 数组.

l f t lft 为例, r g t rgt 求法类似:

枚举回文串中心的位置 i i ,则 l f t [ i , i + n p [ i ] ) lft[i,i+np[i]) 区间内未设置值得位置全都设置成为 i i .

实现代码

#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;
}

算法二.

计算数组 l f t , r g t lft,rgt

l f t [ i ] lft[i] 表示以 i i 为右端点的最长回文子串在 s s 中的实际长度.

r g t [ i ] rgt[i] 表示以 i i 为左端点的最长回文子串在 s s 中的实际长度.

那么答案就是枚举 i i 为偶数, a n s = m a x ( l f t [ i ] + r g t [ i + 2 ] ) ans = max(lft[i] + rgt[i+2])

l f t lft 为例, r g t rgt 类似:

从小到大枚举 i i ,并用一个优先队列维护当前最小的回文串中心点 i i ,设定在 i + n p [ i ] i+np[i] 位置将优先队列中的 i i 设为失效.

每次从优先队列中取出的第一个有效的数即是 l f t [ i ] lft[i] .

这种算法的思路与算法一本质上是一样的,只不过枚举量不同,算法一枚举的是 l f t [ i ] lft[i] ,然后利用单调性将时间复杂度降低到了 O ( n ) O(n)

算法二枚举的是 i i ,需要用数据结构来维护,时间复杂度是 O ( n l o g n ) 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/83094819