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

来源:互联网 发布:sql语句建立table 主键 编辑:程序博客网 时间:2024/04/28 04:19

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


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


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 nodeint 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 noder = 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 rightif (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("#", "");}


有问题欢迎指出。

0 0
原创粉丝点击