求解最长回文子串---manacher算法

manacher求解最长回文子串

#include<bits/stdc++.h>
using namespace std;
char ss[11000100];
char s[22000100];
int p[22000100];
int manacher(int n){
int mid=1,mr=1,ans=-1;
for(int i=1;i<=n;i++){
    if(i<mr){
        p[i]=min(mr-i,p[mid*2-i]);//当当前的位置小于处理过的最右边时,p[i]的值为mr-i和p[mid*2-i],即i点以mid为重点的对称点的最长回文子串的长度
    }
    else p[i]=1;//i>=mr,没有被处理过 p[i]=1
    while(s[i-p[i]]==s[i+p[i]]){//处理以i为中心的最长回文子串的长度,匹配成功长度加一,没有成功则跳出
        p[i]++;
    }
    if(mr<i+p[i]){//当前处理过的位置i+p[i]大于之前处理过的最右边的值时,我们把处理的最右边的位置赋值为i+p[i],把新的mid点位置赋值为i
        mr=i+p[i];
        mid=i;
    }
    ans=max(ans,p[i]-1);//算出最长回文子串的长度,长度即为p[i]-1
}
return ans;
}
int pretreatment(){//预处理把串变为奇数长度
s[0]=s[1]='#';//s[0]也要赋值,因为后面会进行比较以i=1时,s[i-1]和s[i+1]不对称退出
int lenss=strlen(ss+1);
for(int i=1;i<=lenss;i++){
    s[i*2]=ss[i];
    s[i*2+1]='#';
}
return lenss*2+2;
}
int main(){
scanf("%s",ss+1);
int n=pretreatment();
int ans = manacher(n);
printf("%d\n",ans);
}

例2 求解最长双回文子串

最长双回文串
例如 AABBAAACA 可以分为AABBAA 和 ACA 最长双汇问子串的长度为9
思路:记录当前位置的左边和右边的最长回文子串的长度为多少求出加在一起最大的即可

#include<bits/stdc++.h>
using namespace std;
char ss[200010];
char s[400010];
int p[400010];
int l[400010];
int r[400010];
int solve(int n){
int ans=0;
for(int i=1;i<=n;i++){
    ans=max(ans,r[i*2]+l[i*2+2]);
}

return ans;
}
void manacher(int n){
int mr=1,mid=1,ans=-1;
for(int i=1;i<=n;i++){
    if(i<mr){
        p[i]=min(mr-i,p[2*mid-i]);
    }
    else p[i]=1;
    while(s[i-p[i]]==s[i+p[i]]){
        //if(i==8){cout<<s[i]<<" "<<p[i]<<endl;}
        l[i-p[i]]=max(l[i-p[i]],p[i]+1);
        r[i+p[i]]=max(r[i+p[i]],p[i]+1);
        p[i]++;
    }
    if(mr<i+p[i]){
        mr=i+p[i];
        mid=i;
    }

}
int pretreatment(){
s[0]=s[1]='#';
int len=strlen(ss+1);
for(int i=1;i<=len;i++){
    s[i*2]=ss[i];
    s[i*2+1]='#';
}
return len*2+1;
}
int main(){
scanf("%s",ss+1);
int n = pretreatment();
manacher(n);
int ans = solve(n);
printf("%d\n",ans);
}

其它的最长回文子串问题
1.最长回文
题意:给两个长度为n的字符串,使得取出s1的子串[l1,r1]和s2的子串[l2,r2](可以为空串),且r1=l2,求
用这样的方法能得到的最长回文串的长度。
思路:分别求出s1和s2的p数组,然后枚举回文中心,找出是s1在当前位置的最长回文子串长还是s2的,找出长的
那一个,长度为t,(因为中心为i,p[i]是已经求出来的满足的最长回文子串,我们只用取其中最长,取长的那一个
进行匹配)再进行 while(s1[i - t] == s2[i + t-2]){ t++; }向两边进行对比,看是否相同,求出t的最大
值即可   

关键代码:

for(int i=2;i<=2*n;i++){
        int t = max(p1[i], p2[i-2]);
        while(s1[i - t] == s2[i + t-2]){
            t++;
        }
        res=max(res,t);
}

过题代码:

#include<bits/stdc++.h>
using namespace std;
char s[210000];
string s1;
string s2;
int p1[210000],p2[210000];
int manacher(int n,int p[]){
//把数组的地址传过来
int mid=1,mr=1,ans=-1;
for(int i=1;i<=n;i++){
    if(i<mr){
        p[i]=min(mr-i,p[mid*2-i]);//当当前的位置小于处理过的最右边时,p[i]的值为mr-i和p[mid*2-i],即i点以mid为重点的对称点的最长回文子串的长度
    }
    else p[i]=1;//i>=mr,没有被处理过 p[i]=1
    while(s[i-p[i]]==s[i+p[i]]){//处理以i为中心的最长回文子串的长度,匹配成功长度加一,没有成功则跳出
        p[i]++;
    }
    if(mr<i+p[i]){//当前处理过的位置i+p[i]大于之前处理过的最右边的值时,我们把处理的最右边的位置赋值为i+p[i],把新的mid点位置赋值为i
        mr=i+p[i];
        mid=i;
    }
    ans=max(ans,p[i]-1);//算出最长回文子串的长度,长度即为p[i]-1
}

return ans;
}
int pretreatment(string &ss){//预处理把串变为奇数长度
//传入字符串的地址
s[0]=s[1]='#';//s[0]也要赋值,因为后面会进行比较以i=1时,s[i-1]和s[i+1]不对称退出
int lenss=ss.size();
for(int i=1;i<=lenss;i++){
    s[i*2]=ss[i];
    s[i*2+1]='#';
}
ss=s;//把源字符串赋值成变为技术长度之后的字符串
return lenss*2+2;
}
int main(){
int n;
scanf("%d",&n);
cin>>s1>>s2;
s1=" " + s1;
s2=" " + s2;
int len=pretreatment(s1);
int ans = manacher(len,p1);
len=pretreatment(s2);
ans = manacher(len,p2);
int res = 0;
for(int i=2;i<=2*n;i++){
        int t = max(p1[i], p2[i-2]);
        while(s1[i - t] == s2[i + t-2]){
            t++;
        }
        res=max(res,t);
}
printf("%d\n",res-1);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值