马拉车(manacher)算法

本篇博客基于这篇博客的理解。

马拉车可以在线性时间内找到字符串的最大回文子串。

先来说说它是如何工作的。 其实本质上马拉车算是一个很优美的暴力算法,它从头到尾遍历一遍字符串的每一个元素,然后把当前遍历到的元素(我们设为a[i])向左向右扩展,(设扩展半径为r)。那么现在这个a[i]这个元素就是中心元素。

这个算法中有几个比较重要的变量 p【】,mx,id。p【i】表示的是当前以a[i]为中心元素,p【i】是最大回文串的扩展半径。

mx是前i个元素中最大回文子串的右边界。id是记录前i个元素中最大回文子串的中心元素。 所以我们只需遍历一遍数组,中心元素和半径都可以找到,这样最大回文子串的起始位置也就确定下来了。

再来说说最核心的地方。

p[i] = mx > i ? min(p[id * 2 - i],mx - i):1;

当前的mx记录的是(i-1)时的最右边界,如果 i >=mx 意味着以id为中心的最长回文串没有把当前a[i]这个元素包含进去,那么我们就先设这个半径为1(半径为1的意思是当前串只有a[i]这个元素,并不是向左向右扩展1),之后再暴力找。

mx > i :当前元素被包含进来了,这里就不用暴力了。因为我们a[i]是在回文串中,元素是以id为对称轴左右相等的。 我们设j是关于id的i的对称点。那么p【j】的值一定等于p【i】。但是当前要满足(mx-i)> p[j],也就是说a【i】的扩展半径不能超过mx,mx外的值是我们还没有遍历到的。

还有一种就是 (mx-i)>p【j】的时候, a【j】的扩展半径超过了mx-i,还是因为mx外的值无法确定,我们只能确定 a[i]~a[mx]这个区间的元素是回文串

最后一点就是因为存在“abaaba”这种情况,回文串的对称轴不在元素上,我们在中间插入没有用过的字符 变成“#a#b#a#a#b#a#”

主要的介绍完了,我这里没画图,文字描述的比较抽象,没看懂得同学可以移步上文提到的博客。

 

这里主要讲的是自己的理解,代码则是照着大佬的打了一遍 .........。

 

#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <bitset>
#include <time.h>
#define LL long long
#define ULL unsigned long long
#define mod 1000000007
#define INF 0x3ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
#define PI 3.1415927
#define maxn 100005
using namespace std;
int a[maxn];
char b[maxn];
char c[maxn];
int n,d,cnt,sum;
int vis[1005];
int dp[1005];
int manacher(string s)
{
    string t = "%#";
    for(int i = 0; i < s.size(); i++){
        t += s[i];
        t += "#";
    }
    vector<int> p(t.size(),0);
    int mx = 0,id = 0,str_center = 0,str_len = 0;
    for(int i = 1; i < t.size(); ++i){
       p[i] = mx > i ? min(p[id * 2 - i],mx - i):1;
       while(t[i + p[i]] == t[i - p[i]]) ++p[i];
       if(mx < p[i] + i){
         mx = p[i] + i;
         id = i;
       }
       if(str_len < p[i]){
         str_len = p[i];
         str_center = i;
       }
    }
    return str_len - 1;

}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        string s;
        cin >> s;
        printf("%d\n",manacher(s));
    }


    return 0;

}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值