【ACWing】3188. manacher算法

题目地址:

https://www.acwing.com/problem/content/description/3190/

给定一个长度为 n n n的由小写字母构成的字符串,求它的最长回文子串的长度是多少。

输入格式:
一个由小写字母构成的字符串。

输出格式:
输出一个整数,表示最长回文子串的长度。

数据范围:
1 ≤ n ≤ 1 0 7 1≤n≤10^7 1n107

可以用Manachar算法。先将整个字符串做如下处理,将这个字符串每两个字符之间插入一个不在字母表里的字符,比如#,然后在首尾也添加#,再在首尾添加两个不同字符,不能是字母表或者是#,比如可以在开头添加$结尾添加^。例如,对于字符串aab,处理完之后字符串就变为了$#a#a#b#^。令处理完的字符串为 t t t。那么,每个 s s s里的回文子串都能对应于 t t t的某个长度为奇数的回文子串(我们要求在 t t t里取的子串以#开头结尾),接下来考虑对 t t t求每个位置为中心的情况下最大的回文子串半径,设 p [ i ] p[i] p[i]为以 t [ i ] t[i] t[i]为中心的最长回文子串的半径, p [ 0 ] = 0 p[0]=0 p[0]=0,为无效值。同时维护两个变量 m r mr mr m i mi mi,分别是右端点最靠右的最长回文子串的中点位置和右端点位置 + 1,接下来递推。从左向右遍历到 t [ i ] t[i] t[i],如果 i < m r i<mr i<mr,那么找到 j = 2 m i − i j=2mi-i j=2mii,我们证明 p [ i ] ≥ min ⁡ { p [ j ] , m r − i } p[i]\ge \min\{p[j], mr-i\} p[i]min{p[j],mri}。如果 p [ j ] ≤ m r − i p[j]\le mr-i p[j]mri,那么以 i i i为中点的最长回文子串半径 p [ i ] p[i] p[i]显然可以取到 p [ j ] p[j] p[j],并且在以 j j j为中点的最长回文子串到达边界的时候, p [ i ] p[i] p[i]有可能更大;如果 p [ j ] > m r − i p[j]>mr-i p[j]>mri,那么以 i i i为中点的最长回文子串右边界可以取并且最多取到 m r mr mr,否则就会得出 m r mr mr可以继续拓展的结论,矛盾。无论怎样, p [ i ] p[i] p[i]都可以从 min ⁡ { p [ j ] , m r − i } \min\{p[j], mr-i\} min{p[j],mri}开始递增枚举。如果 i ≥ m r i\ge mr imr,那么 p [ i ] p[i] p[i]可以从 1 1 1开始枚举。当枚举停下来的时候(即再扩大就不回文的时候),更新 m r mr mr m i d mid mid。求得的所有 p [ i ] p[i] p[i]的最大值如果是 m p mp mp的话,容易验证, m p − 1 mp-1 mp1就是 s s s的最长回文子串的长度。代码如下:

#include <iostream>  
#include <cstring>
using namespace std;

const int N = 2e7 + 10;
int n;
char a[N], b[N];
int p[N];
int res;

// 构造新串
void init() {
    int k = 0;
    b[k++] = '$', b[k++] = '#';
    for (int i = 0; i < n; i++) b[k++] = a[i], b[k++] = '#';
    b[k++] = '^';
    n = k;
}

void manacher() {
    int mr = 0, mid;
    // 最后的'^'就不用枚举了
    for (int i = 1; i < n - 1; i++) {
    	// 初始化p[i]
        if (i < mr) p[i] = min(p[mid * 2 - i], mr - i);
        else p[i] = 1;
		
		// 向外扩展
        while (b[i - p[i]] == b[i + p[i]]) p[i]++;

        if (i + p[i] > mr) {
            mr = i + p[i];
            mid = i;
        }

        res = max(res, p[i] - 1);
    }
}

int main() {
    scanf("%s", a);
    n = strlen(a);
    init();
    manacher();
    
    printf("%d\n", res);
    return 0;
}

时空复杂度 O ( n ) O(n) O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值