Manacher算法

目录

Manacher算法的介绍

回文半径与回文直径的详解

Manacher算法的详解

Manacher算法的介绍

       Manacher算法解决的问题是在字符串str中,最长回文子串的长度如何求解?如何做到时间复杂度O(N)完成?

       对于这个问题的经典解法是,首先将字符串的每两个字符之间都加入一个辅助字符,同时开头和结尾位置也加入。对这个新的字符串计算它的最长回文子串,从第一个字符开始,以自身作为对称轴往左右两边扩充,如果左右两边的字符相同,继续扩充,如果不同,停止扩充。然后记录它的回文子串长度,继续求解其它字符,直到最后一个字符求解完毕。将这时的字符串的每个字符的回文子串长度进行比较,选出最长的然后除以2取整得到的就是原字符串的最长回文子串长度。但是采用这种方式的话,时间复杂度为O\left ( N^{2} \right ),而Manacher算法就可以在时间复杂度O\left ( N \right )内完成这个操作。

回文半径与回文直径的详解

        回文子串的长度叫做回文直径,根据回文直径的长度得到回文半径。同时记录最大回文右边界,也就是回文子串的最右侧位置。

Manacher算法的详解

       Manacher算法利用的也是经典算法的操作思想,只是在步骤上进行了加速和优化。利用Manacher算法对字符串的最大回文子串长度的求解,实则是在求解处理后字符串的最大回文半径,对每一个字符的回文半径进行求解,然后得出最大的回文半径减去1即可得到原字符串的最大回文子串长度。

       首先分为两种大的情况,第一种大的情况就是要求的字符的位置在最大回文右边界外面,那么此时只能利用暴力尝试的方式往外扩充。

       第二种大的情况就是要求的字符的位置在最大回文右边界里面,那么第二种大的情况又可以分为三种小的情况,我们求解时利用的变量有最大回文右边界,记为R,整个回文子串的左边界记为L,中心点记为C,i位置关于中心点C的对称点记为i’。第一种小情况是,i位置的对称点i’的回文子串在L..R内,那么此时i位置的回文子串的长度就是i’位置的回文子串的长度,因为它们是回文的,关于中心点对称。第二种小的情况是,i’的回文子串位于L..R边界位置,那么此时i位置的回文子串在i’回文子串长度的基础上进行外扩。第三种小的情况是,i'的回文子串超出L..R,那么此时i位置的回文子串的长度就是R-i,因为首先i和i'是对称的,那么在区域范围内的回文子串的长度是相同的,而L..R的范围之所以没有继续外扩,是因为R右面的字符和L左边的字符不同,所以无法外扩,所以i位置的回文子串也无法外扩。

        对于这个算法的时间复杂度进行分析,时间复杂度为O\left ( N \right )

    public static char[] manacherString(String str) {//将字符串变成处理后的加上特殊字符的数组
		char[] charArr = str.toCharArray();
		char[] res = new char[str.length() * 2 + 1];//处理串的长度等于原字符串长度的2倍+1
		int index = 0;
		for (int i = 0; i != res.length; i++) {
			res[i] = (i & 1) == 0 ? '#' : charArr[index++];
		}
		return res;
	}
    //对于这个算法的时间复杂度进行分析为O(N)
	public static int maxLcpsLength(String str) {
		if (str == null || str.length() == 0) {
			return 0;
		}
		char[] charArr = manacherString(str);//将字符串变成处理后的加上特殊字符的数组
		int[] pArr = new int[charArr.length];//回文半径数组
		int C = -1;//中心
		int R = -1;//回文右边界再往右一个位置,也就是最右的有效区到了R-1位置
		int max = Integer.MIN_VALUE;//扩出来的最大值
		for (int i = 0; i != charArr.length; i++) {//每一个位置都求回文半径
			pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
//记录至少不用检验的区域
//如果i在R外面,那么这时候需要暴力尝试,至少不用检验的区域只有自己,也就是1,而如果i在R内,那么分为三种情况,当i的对称点i’的回文区域在L..R内,那么此时不用检验的回文区域就是i’的最大回文串长度,也就是pArr[2*C-i];当i的对称点i’的回文区域正好在边界位置,那么此时不用检验的区域为R-i;而如果i’的回文区域超出了L..R区域,那么此时i不用检验的区域也是R-L。
			while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {//没有超出数组长度
				if (charArr[i + pArr[i]] == charArr[i - pArr[i]])//四种情况,统一都往外扩,反正对于不需要往外扩的情况,扩一次也会失败
					pArr[i]++;
				else {
					break;
				}
			}
			if (i + pArr[i] > R) {//如果回文子串的最右边界更往右了,那么继续往外扩
				R = i + pArr[i];
				C = i;
			}
			max = Math.max(max, pArr[i]);//得出最长的回文子串长度
		}
		return max - 1;//处理串和原原字符串的关系就是,处理串的半径的长度-1就是原字符串最大回文子串的长度
	}
  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值