Manacher算法求最大回文串

本文详细介绍Manacher算法,一种高效寻找字符串中最长回文子串的方法。通过预处理原字符串并利用回文串的对称性,算法能在O(n)的时间复杂度内解决问题。文中提供了核心代码及两道例题的解决方案。

在处理回文串中,奇数回文和偶数回文是一个神烦的问题,而Manacher算法巧妙地解决了这个问题。

首先,将原串进行下述处理:

1. 首尾添加分隔符,如 '#'

2. 每两个字符间添加分隔符

3.经过1 2步后,在字符串的首尾添加结束符(任意符号,加以区别,而且首尾符号不同)

例如:

原串 abcba

新串 $#a#b#c#b#a#@

这样所有的情况都变成了奇数回文串的情况。

接着用一个辅助数组p[]来存以新串的每个字符为回文中心,构成回文串的最大半径,初始为1。

如:

下标 0  1  2  3  4  5  6  7  8  9  10 11 12

新串 $  #  a  #  b  #  c  #  b  #   a    #   @

p[ ]       1   2  1  2  1  6  1  2  1   2   1

回文串的最大长度就是p[i] - 1的最大值。

核心代码:

        for(int i = 1; str2[i]; i++)//id是回文中心,Maxid是以id为回文中心,p[id]为回文半径,回文范围最远的位置
        {
            if(Maxid > i)
                p[i] = min(p[(id << 1) - i], Maxid - i);*
            else
                p[i] = 1;
            while(str2[p[i] + i] == str2[i - p[i]])
                p[i]++;
            if(p[i] + i > Maxid)
            {
                Maxid = p[i] + i;
                id = i;
            }
        }
*代码解释: p[i] = min(p[(id << 1) - i], Maxid - i);

根据回文串的对称性,在回文串中,在Maxid范围内时,对称两点的对称性也是相同的,但是当超出Maxid范围时,对称性就不一定了,所以对称半径最多只到Maxid。

如图:



例题:

hdoj : http://acm.hdu.edu.cn/showproblem.php?pid=3068

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M = 11e4 + 20;
char str1[M], str2[M << 1];
int p[M << 1];
main()
{
    while(~scanf("%s", &str1[1]))//从1开始
    {
        int Maxid = 0, id = 0, ans = 0;
        str1[0] = '#';//覆盖原来的'\0'
        for(int i = 1; str1[i]; i++)
        {
            str2[i << 1] = str1[i];
            str2[(i << 1) + 1] = '#';
        }
        str2[0] = '$';
        str2[1] = '#';
        str2[(strlen(str1) << 1) + 2] = '\0';
        for(int i = 1; str2[i]; i++)
        {
            if(Maxid > i)
                p[i] = min(p[(id << 1) - i], Maxid - i);
            else
                p[i] = 1;
            while(str2[p[i] + i] == str2[i - p[i]])
                p[i]++;
            if(p[i] + i > Maxid)
            {
                Maxid = p[i] + i;
                id = i;
            }
            if(p[i] > ans)
                ans = p[i];
        }
        printf("%d\n", ans - 1);
    }
}

例题:

hdoj : http://acm.hdu.edu.cn/showproblem.php?pid=4513

思路:

这题是在manacher上的变形,关键是上升序列的处理方式。看代码比较直观。

#include<cstdio>
#include<algorithm>
using namespace std;
const int M = 1e5 + 20;
int human[M * 2], p[M * 2];
main()
{
    int n, Tcase;
    scanf("%d", &Tcase);
    while(Tcase--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            int a;
            scanf("%d", &a);
            human[i * 2] = a;
            human[i * 2 + 1] = -3;
        }
        human[n * 2 + 2] = -1;
        human[1] = -3;
        human[0] = -2;
        int maxid = 0, ans = 0, id = 0;
        for(int i = 1; i < n * 2 + 2; i++)
        {
            if(maxid > i)
                p[i] = min(p[id * 2 - i], maxid - i);
            else
                p[i] = 1;
            int s;
            if(human[i] == -3)
                s = human[i + 1];
            else
                s = human[i];
            while(human[i - p[i]] == human[i + p[i]])
            {
                if(human[i - p[i]] == -3)
                    p[i]++;
                else if(human[i + p[i]] <= s)
                {
                    s = human[i + p[i]];
                    p[i]++;
                }
                else
                {
                    break;
                }
            }
            if(p[i] + i > maxid)
            {
                maxid = p[i] + i;
                id = i;
            }
            if(p[i] > ans)
                ans = p[i];
        }
        printf("%d\n", ans - 1);
    }
}
/*
50
3
51 52 51
4
51 52 52 51
5
50 51 52 51 50
5
52 51 50 51 52
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值