manacher算法 +例题

Manacher

参考博客 (懒惰的我):

简单而有通俗的讲解,讲的太好了
证明
对于一些我的理解,我会以代码注释的形式写在代码里, 我不懒

模板

char  str[maxn];
char temp[(maxn <<1) + 10];//扩展后的字符串
int Len[(maxn <<1 ) + 10];//扩展后字符串第i个位置回文串从中间到第有边界的长度
//相当于 回文子串长度 / 2 + 1
//在用左移运算符的时候要注意 << 比 + 的优先级要低,要加上括号..

inline int init(int len)
{
    temp[0] = '@';//防止越界
    for(int i  = 1; i <= 2 * len; i += 2){
        temp[i] = '#';
        temp[i + 1] = str[i / 2];
    }
    temp[2 * len + 1] = '#';
    temp[2 * len  + 2] = '$';
    temp[2 * len + 3] = 0;
    return 2 * len + 1;
}

int  manacher(int len,int &pos)
{
    int mx = 0, ans = 0, po = 0;
    //mx为当前计算的回文字串最右端的字符位置再  + 1
    //po为mx对应的回文子串的中点位置,也即得到mx为终点的
    //回文子串的起始位置
    for(int i = 1; i <= len; i++){
        //
        if(mx > i)    Len[i] = min(mx - i , Len[2 * po - i]);
        else Len[i] = 1;
        //这里结合了mx == i和mx  < i的情况,都是要继续暴力找
        //如果第一种情况,由于回文串的性质,第一次循环条件不会成立
        while(temp[i - Len[i]] == temp[i + Len[i]]) Len[i]++;
        //如果当前回文子串的右边界 > mx那就更新mx
        if(i + Len[i] > mx){
            po = i;
            mx = i + Len[i];
        }
        if(ans < Len[i]){
            ans = Len[i];
            pos = i;//找最大长度子串的位置
        }
    }
    return ans - 1;
}

hdu3294

这里说一下扩展后串的位置和原串位置的关系,图上是从0开始,从1开始也是可以的
图片原文链接
在这里插入图片描述

这里求一下最长子串的位置就可以了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <cmath>
#include <cstdlib>
#include <stack>
 
using namespace std;
 
const int maxn = 200000 + 5;
const int Size = 20;
 
char  str[maxn];
char temp[(maxn <<1) + 10];//扩展后的字符串
int Len[(maxn <<1 ) + 10];//扩展后字符串第i个位置回文串从中间到第有边界的长度
//相当于 回文子串长度 / 2 + 1
//在用左移运算符的时候要注意 << 比 + 的优先级要低,要加上括号..

inline int init(int len)
{
    temp[0] = '@';//防止越界
    for(int i  = 1; i <= 2 * len; i += 2){
        temp[i] = '#';
        temp[i + 1] = str[i / 2];
    }
    temp[2 * len + 1] = '#';
    temp[2 * len  + 2] = '$';
    temp[2 * len + 3] = 0;
    return 2 * len + 1;
}

int  manacher(int len,int &pos)
{
    int mx = 0, ans = 0, po = 0;
    //mx为当前计算的回文字串最右端的字符位置再  + 1
    //po为mx对应的回文子串的中点位置,也即得到mx为终点的
    //回文子串的起始位置
    for(int i = 1; i <= len; i++){
        //
        if(mx > i)    Len[i] = min(mx - i , Len[2 * po - i]);
        else Len[i] = 1;
        //这里结合了mx == i和mx  < i的情况,都是要继续暴力找
        //如果第一种情况,由于回文串的性质,第一次循环条件不会成立
        while(temp[i - Len[i]] == temp[i + Len[i]]) Len[i]++;
        //如果当前回文子串的右边界 > mx那就更新mx
        if(i + Len[i] > mx){
            po = i;
            mx = i + Len[i];
        }
        if(ans < Len[i]){
            ans = Len[i];
            pos = i;//找最大长度子串的位置
        }
    }
    return ans - 1;
}

int main()
{
    char q[2];
    while(scanf("%s%s",q,str) == 2){
        char c = q[0];
        int len = strlen(str);
        int nlen = init(len);
        int pos = -1;
        int ans = manacher(nlen,pos);
        if(ans < 2){
            printf("No solution!\n");
            continue;
        }
        //最长回文子串在原字符串中的左右边界
        //因为每一个回文子串结尾的左右两端都是'#'
        //那么我们左右都找ans-1个字符保证这时候
        //这个字符串左右两端都是原串字符这样直接除2就可以得到最长子串的左右端点
        int l = (pos - ans  + 1)/ 2  - 1;
        int r = (pos + ans  - 1)/ 2  - 1;
        printf("%d %d\n",l,r);
        for(int i = l; i <= r; i++){
            int x = c -'a';
            if(str[i] - x >= 'a')   printf("%c",str[i] - x);
            else   printf("%c",str[i] + 26 -x);
        }
        printf("\n");
    }
    return 0;
}

hdu3068

裸题

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <string>
#include <cmath>
#include <cstdlib>
#include <stack>
 
using namespace std;
 
const int maxn = 110000 + 5;
const int Size = 20;
 
char  str[maxn];
char temp[maxn<<1 + 10];
int Len[maxn<<1  + 10];

inline int init(int len)
{
    temp[0] = '@';
    for(int i  = 1; i <= 2 * len; i += 2){
        temp[i] = '#';
        temp[i + 1] = str[i / 2];
    }
    temp[2 * len + 1] = '#';
    temp[2 * len  + 2] = '$';
    temp[2 * len + 3] = 0;
    return 2 * len + 1;
}

int  manacher(int len)
{
    int mx = 0, ans = 0, po = 0;
    //mx为当前计算的回文字串最右端的字符位置再  + 1
    //po为mx对应的回文子串的中点位置,也即得到mx为终点的
    //回文子串的起始位置
    for(int i = 1; i <= len; i++){
        if(mx > i)    Len[i] = min(mx - i , Len[2 * po - i]);
        else Len[i] = 1;
        while(temp[i - Len[i]] == temp[i + Len[i]]) Len[i]++;
        if(i + Len[i] > mx){
            po = i;
            mx = i + Len[i];
        }
        ans = max(ans,Len[i]);
    }
    return ans - 1;
}

int main()
{
    while(scanf("%s",str) == 1){
        int len = strlen(str);
        int nlen = init(len);
        int ans = manacher(nlen);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值