欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Manacher算法解决最长回文串问题

程序员文章站 2022-07-13 22:06:51
...

        如果求一个字符串,求字符串中最长连续回文子串问题,如果用暴力求解的话会使得时间复杂度为O(n^2),因为暴力方法在以不同字符为中心往两边扩的时候,每次都是往两边扩,那么其中有好多重复扩的部分。而本文讲解的manacher算法就是使得前面将前面扩的部分备份,后面扩的时候便于省去不需要重复扩的部分,从而降低时间复杂度,使得时间复杂度大约为O(n)。首先先说明manacher算法的三个重要的变量,pArr数组,用于存储以每一个位置为中心的最大回文半径;right表示右边界,即当前位置之前扩到字符的最右边界;index表示扩到right时对应的字符位置。在进行算法之前,需要对算法进行预处理,为了避免奇偶性,所以在该字符串中穿插一个标记字符,比如'#',在字符串首尾以及每两个字符之间插入一个标记字符。

        然后进行讨论manacher的算法思路:

1.如果当前位置i在right之外,则不断往两边扩,扩到不是回文为止。

2.如果i在right之内,则需要讨论几种情况:这时候需要找到i关于index的对称位置:2*index-i,并判断pArr[2*index-i]与right-i的关系,如果前者小于后者,那么pArr[i]=pArr[2*index-i],并且不需要再往外扩,只需要接着判断下一个i即可;还有种情况便是pArr[2*index-i]大于等于right-i,这时候需要截取的是right-i的部分,因为right之后的字符是未知的,所以需要以i为中心,并从半径right-i开始往两边扩,所有情况扩的过程不仅要满足字符对称相等,还需要满足边界问题。假设扩的半径为r,则需要满足i-r>=0同时满足i+r<length.我们首先来分析一下比较复杂的情况,就是i关于index对称位置的左边界在index为中心的左边界的左边的情况,如下图所示:

Manacher算法解决最长回文串问题

        如上图所示的字符串的当前index和right如图所示,假设目前的i位置如图所示,该位置的对称位置i'如图所示,图中可知i'的左边界超出了index的左边界,因此不能直接把i'的回文半径给i位置使用,因为i位置的right之后是位置的。但是可以保证的是2*i-right-->i-->right部分是回文串,根据这段关于index对称到左边对应的是i'为中心的最大回文串的子回文串,所以可以将开始扩的位置定位到right+1开始。所以可以看出,当i<right的时候,可以总结为在pArr[2*index-i]和pArr[index]+index-i之间取较小值即可。假设i位置的半径最终为r,如果i+r-1>right的时候需要更新right的index值。注意r是局部变量,每遍历一个i值时,r都从1开始。

具体代码如下所示:

import java.util.*;
public class Manacher {

	public static int manacher(String str)
	{
		//首先进行预处理。
		int n=str.length();
		StringBuffer sb=new StringBuffer();
		sb.append("#");
		for(int i=0;i<n;i++)
		{
			sb.append(str.charAt(i)).append("#");
		}
		int[] pArr=new int[2*n+1];
		int right=-1;//为了后续更容易判断,从-1开始。
		int index=-1;
		for(int i=0;i<sb.length();i++)//开始逐个遍历求以各个字符为中心的最大回文半径。
		{
			int r=1;//每个字符最少半径为1.
			if(i<=right)//判断是够在right之内,如果是则寻找开始扩充的边界,如果不是则直接从当前字符往两边扩充。
			{
				r=Math.min(pArr[2*index-i], pArr[index]+index-i);//需要取小值,因为对称点回文串左边界可能在index左边界之外。对于当前点来说它已知的边界不能超过right。
			}
			//开始扩充,扩充的时候需要在保证相等的时候注意边界。
			while(i-r>=0&&i+r<sb.length()&&sb.charAt(i+r)==sb.charAt(i-r))
				r++;
			pArr[i]=r;
			//扩充完之后需要检查是否需要更新index和right
			if(i+r-1>right)
			{
				right=i+r-1;
				index=i;
			}
			
		}
		StringBuffer sb1=new StringBuffer();
		int maxnum=1;
		int k=0;
		for(int i=0;i<pArr.length;i++)
		{
			if(pArr[i]>maxnum)
			{
				maxnum=pArr[i];
				k=i;
			}
		}
		System.out.println(k);
		for(int i=k-maxnum+1;i<k+maxnum;i++)
		{
			if(sb.charAt(i)!='#')
				sb1.append(sb.charAt(i));
		}
		
		System.out.println(sb1.toString());
		return sb1.length();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String str="baabbaaa";
		System.out.println(manacher(str));

	}

}

相关标签: Manacher