manacher算法



回文串在笔试编程题中很常见,昨天在牛客网上听了左神讲解的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 stub
		
		Scanner 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 stub
		
		Scanner scn = new Scanner(System.in);
		String str = scn.nextLine();
		
		LongestPalindromicSubstring(str);

	}
}

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值