最长回文字符串——Manacher(马拉车)算法

给定一个字符串,求出其最长回文子串。例如:
s=“babad”,最长回文字符串:“bab”。(“aba” 也是一个有效答案)
根据Manacher算法得出来的答案为"aba"。

思路:
1.进行新的字符串进行装载
babad 装载后 $#b#a#b#a#d#
为什么需要装载?
为了消除奇回文与偶回文,让偶数回文数能有中心点标记位置
2.定义一个辅助数组int p[](记录的数字为最长回文字符串的长度,也是往两边移动的格数)

i 0 1 2 3 4 5 6 7 8 9 10 11
str $ # b # a # b # a # d #
p[i] 0 1 2 1 4 1 4 1 2 1 2 1

3.设置两个变量,mx 和 id 。mx 代表以 id 为中心的最长回文的右边界,也就是mx = id + p[id]。
算法的核心:

if(i < mx) {
	p[i] = Math.min(p[2*id-i],mx-i);
}

遍历到 i 时如果 i < mx 代表该节点包含在目前最长字符串的里面(且是右后方),所以判断当前节点与mx的距离 mx-i 与中心点id对称点的 p[2*id-i] 的最小值(1.对称点的值不够到mx,2.对称点的值大于mx,但i不能继续了),后面再继续进行对比更新当前节点的p[i]值。

while(i+p[i] < len && str.charAt(i-p[i]) == str.charAt(i+p[i])) {
	p[i]++;
}

下面就是实现代码:

java

public static String Manacher(String s) {
		StringBuffer str = new StringBuffer();
		str.append("$#");  //$防止数组越界
		for(int i = 0;i < s.length();i++) {
			str.append(s.charAt(i));
			str.append("#");
		}
		int mx = 0; //id为对称点的最右边界
		int id = 0; //对称点
		int len = str.length(); 
		int[] p = new int[len];
		int temp = -1; //最长字符串长度
		int tid = 0; //最长字符串的中心点位置
		for(int i = 1;i < len;i++) {
			if(i < mx) {
				p[i] = Math.min(p[2*id-i],mx-i);
			} else {
				p[i] = 1;
			}
			while(i+p[i] < len && str.charAt(i-p[i]) == str.charAt(i+p[i])) {
				p[i]++;
			}
			if(mx < i + p[i]) {
				id = i;
				mx = i + p[i];
			}
			if(temp < p[i]-1) {
				temp = p[i]-1;
				tid = i;
			}
			if(p[i]*2 >= len)
				break;
		}
		return str.toString().substring((tid-p[tid]+1),(tid+p[tid]-1)).replaceAll("#", "");
	}

时间复杂度:O(n) (以空间来换时间)

你可能感兴趣的:(算法)