您的位置:首页 > 编程语言 > Go语言

找到最长回文字符串 - Manacher's Algorithm

2016-10-03 09:22 465 查看
记得刚开始学习计算机的时候,除了输出星星就是找到最长回文字符串这样的问题, 开始以为最长回文串问题很简单,但是经过多年的学习再回头看的时候发现,它并不简单,今天就给它解个密。

开始我们先来看一个时间复杂度差的算法。 

1. 时间复杂度为 n^3.    空间复杂度为 1.  这个算法基本上是最简单的了,最好理解的。

/**
* 使用iteration
* 循环三次
* 	 	第一次(最外边循环 : 从最长的长度开始,循环长度,每次减1
* 		第二次 (中间循环): 对每个长度,循环最长字符串的起始点,起点从0每次加1
* 		第三次 (最内循环): 对每个长度每个起点,对左右进行比较,每次左+1,右-1
*
*
*
*
* */
public static String longestP (String S) {
if (S.length() < 2 || S == null) { return S; }
int length = S.length();
int left = 0;
int right = 0;
while (length >= 0) {

for (int i = 0; i + length - 1 < S.length(); i++) {
left = i;
right = i + length - 1;

while (left < right) {
if (S.charAt(left) == S.charAt(right)) {
left ++;
right --;
continue;
} else {
break;
}
}

if (left >= right) { return S.substring(i, i+ length); }
}

length --;
}

return "";
}


2. Manacher's Algorithm - 时间复杂度为 N ( 应该大于N 小于 N ^ 2)
这个算法需要一个预处理和主要的三个变量。

预处理: 将输入字符串的各个字符使用特殊字符进行 分割,比如 #。   e.g.  input: abba 处理: #a#b#b#a#

三个主要变量: 

1. 一个和预处理之后大小一样的 int 数组 rad[] - 用来存储以当前字符为中心的最长子回文字符串的半径。

2. 一个整形变量来存储- 我们遍历过的所有子回文字符串能触及到的最右边的位置的maxRight。

3. 另一个整形变量来存储 - 2.中maxRight所对应的 子回文字符串的中心点的位置pos。

每次遍历从以当前字符为中心向两头扩展,扩展的半径是多少?

这就是个问题。。。

解决办法, 通过对比 i 和 maxRight, 当i < maxRight 的时候, 判断 maxRight - i 的大小 和  一个点 j ( 这个点是 i 以 pos为对称的  对称点 )的最长回文半径的大小(存储在rad 中). 

使用较小值作为半径, 从当前点开始进行两头扩张。每次扩张成功之后半径+1。

public static String Manacher (String S) {
if (S.length() < 2 || S == null) { return S; }

//construct new string
//basically, insert one # to the String S,
//make sure each character is surrounded by #
StringBuilder newS = new StringBuilder();
newS.append("#");

int start = 0;
while (start < S.length()) {
newS.append(S.charAt(start));
newS.append("#");

start ++;
}

int [] rad = new int[newS.length()];	//to store the radius of a string with pivot of current node
int maxRight = -1;	//most right we can touch.
int pos = -1;		//the position for which node that can touch the most right.

for (int i = 0; i < newS.length(); i++) {
int r = 1; 	//radius

if (i <= maxRight) {
//since i is less than the max right,
//so we can compare the radius of the node which is symmetric with pos and centered on current node
r = Math.min(rad[2*pos - i], maxRight - i);
}

//extend the string, compare with the i - r(left) and i + r (right)
//if they equal, so that radius ++
while (i - r >= 0 && i + r < newS.length()
&& newS.charAt(i-r) == newS.charAt(i+r)) {
r++;
}

//if i + r -1 is greater than the most right that the most right we can touch.
//we have to update the max right and the position of max right
if (i + r - 1 > maxRight) {
maxRight = i + r - 1;
pos = i;
}

rad[i] = r;
}
int MaxR = 0;
int pivot = 0;
for (int i = 0; i < rad.length; i++) {
if (rad[i] > MaxR) {
MaxR = rad[i];
pivot = i;
}

}
return newS.substring(pivot - MaxR + 1, pivot + MaxR).replace("#", "");
}


有问题欢迎指出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: