求最长回文子串(Manacher)算法

本文详细介绍马拉车(Manacher)算法,一种高效的寻找字符串中最长回文子串的方法。通过巧妙地预处理输入字符串并在中心扩展的基础上利用已知的回文信息,算法能够减少不必要的比较,实现O(n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package test;

//Manacher算法,马拉车算法求最长回文子串
//算法基本要点:首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:
//在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 
//为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。
//然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),
//P[i]-1正好是原字符串中回文串的总长度
//计算P[i],该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。
//这个算法的关键点就在这里了:如果mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)。

public class Main {
	
	//预处理
	static void perProcess(String str,char[]strArry){
			strArry[0]='$';
			strArry[strArry.length-1] = '@';
			for(int i =0;i<str.length();i++){
				strArry[i*2+1] = '#';
				strArry[i*2+2] = str.charAt(i);
			}
			strArry[str.length()*2+1] = '#';
			System.out.println(strArry);
			
		}
	
	//马拉车算法
		static void Manacher(char[] strArry,int[] p,String str){
		int id=0,mx=0;//id是最长回文子串的中心,mx是id+p[id],即最长回文子串的右边界。
		for(int i = 1;i<strArry.length-1;i++){
			//求p[i]
			if(mx>i){
				p[i] = Math.min(mx-i, p[2*id-i]);//简介在下面
			}else {
				p[i] = 1;
			}
			while (strArry[i+p[i]]==strArry[i-p[i]]) {
				++p[i];
			}
			if(mx<i+p[i]){
				mx = i+p[i];
				id=i;
			}
		}
		int length=0,center=0;
		for(int i = 0;i<p.length;i++){
			if(length<p[i]){
				length=p[i];
				center=i;
			}
		}
		System.out.println(center+""+length);
		System.out.println(str.substring((center-length)/2, (center+length-1)/2));
		}
    public static void main(String[] args) {
    	String str="19234543278";
    	int[] p = new int[str.length()*2+3];
    	char[] strArry = new char[str.length()*2+3];
    	perProcess(str,strArry);
    	Manacher(strArry, p, str);
    }
    	
    	
     
}

p[i] = Math.min(mx-i, p[2*id-i])的意思是:

在以id为中心的一串回文中,mx为其右边界,2*id-i是i以id为中心的对称点,如果p[2*id-i]>mx-i,说明以i为中心的回文串的右边界最少可以到mx,即p[i]最小为mx-i,反之也成立。

参考文章:

https://www.zhihu.com/question/40965749/answer/152396279

http://www.cnblogs.com/biyeymyhjob/archive/2012/10/04/2711527.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值