manacher算法

来源:互联网 发布:鱼鹰软件 编辑:程序博客网 时间:2024/06/11 20:58


回文串在笔试编程题中很常见,昨天在牛客网上听了左神讲解的manacher算法,受益匪浅,下面就是我整理的manacher算法。


manacher算法是用来求一个字符串中的最长回文子串。


求字符串中的最长回文子串有两种方法:
(1)暴力求解法
   即从左往右遍历字符串,对每一个点进行扩,求出每一个点的回文子串,找出最长回文子串,时间复杂度为O(n^2)
下面是用Java实现的:



package com.example.test;import java.util.Scanner;public class Test5 {static void LongestPalindromicSubstring(String str){char[] buf = str.toCharArray();int len = buf.length;int max = 0;String ans = new String() ;for(int i=0 ; i<len ; ++i){int j = i-1;int k = i+1;int cur = 0;while( j>=0 && k<len && buf[j]== buf[k]){cur++;j--;k++;}cur = cur*2+1;if(cur > max){max = cur;ans = str.substring(j+1,k);}}System.out.println(ans);}public static void main(String[] args) {// TODO Auto-generated method stubScanner scn = new Scanner(System.in);String str = scn.nextLine();LongestPalindromicSubstring(str);}}


(2)manacher算法:
    从一个中心点向两边扩的时候会有奇回文和偶回文,为了统一解决这两种情况,我们可以给所求字符串的两端,和字符之间加上一个统一的字符,如:字符串abcdcbd,变成了#a#b#c#d#c#b#d#。这样转化后我们所求得的回文数除以2就是原始长度。
manacher算法的三个重点:
①回文半径数组。每一个点往外扩都会有相对的回文半径,将其回文半径存放在一个数组中。
②回文最右边界。每一个点在向外扩时,都会有右边界,如何某个点的右边界比先前的右边界往更右的方向。则我们应该更新最右边界。如下图扩a点时其回文右边界在第一个右括号处,扩b点时其回文右边界在第二个右括号处,扩b点时,最右回文边界被更新。


③回文最右边界中心。与②一致。需要注意的是,当两个或多个点的最右边界相同时,记录最早的那个。
manacher算法:分4种情况  时间复杂度O(n)
①当前点没有在回文最右边界里,扩。

②当前点i在回文最右边界里,其对称点i'在LR内,不扩,arr[i] = arr[i'];


③当前点i在回文最右边界里,其对称点i'在LR外,不扩,arr[i] = R-i;


④当前点i在回文最右边界里,其对称点i'压线,扩,因为无法确定



Java代码实现:


package com.example.test;import java.util.Scanner;public class Test5 {static void LongestPalindromicSubstring(String str){StringBuffer buffer = new StringBuffer();buffer.append("#");char[] buf = str.toCharArray();for(int i=0 ; i<buf.length ; ++i){buffer.append(buf[i]+"#");}buf = buffer.toString().toCharArray();int len = buf.length;int[] arr = new int[len];int rr = -1;int center = -1;for(int i=0 ; i<len ; ++i){int j = i-1;int k = i+1;int cur = 0;if(i > rr){    //没有在回文最右边界里,扩while( j>=0 && k<len && buf[j] == buf[k]){cur++;j--;k++;}arr[i] = cur;}else{         //在回文右边界里int ll = center - arr[center];int lcur = center - (rr-center);int llcur = lcur-arr[lcur];if( llcur > ll){         //当前节点的对称点的范围在LR中,不扩arr[i] = arr[lcur];}else if( llcur < ll){    //当前节点的对称点的范围在LR外,不扩arr[i] = rr-i;}else{while( j>=0 && k<len && buf[j] == buf[k]){  //当前节点的对称点的左范围和L重叠,扩cur++;j--;k++;}arr[i] = cur;}}if( i+cur > rr){rr = i +cur;center = i;}}int max = 0;for(int i=0;i<len;++i){if(arr[i]>max){max = i;}}System.out.println(str.substring((max-arr[max])/2, (max+arr[max])/2));}public static void main(String[] args) {// TODO Auto-generated method stubScanner scn = new Scanner(System.in);String str = scn.nextLine();LongestPalindromicSubstring(str);}}

上面的图画的有点丑,应该可以看懂


原创粉丝点击