(Manacher算法)最长回文子串

题目描述:

 

一.中心扩展法

考虑到回文串是沿中心轴对称的,所以沿轴向两端扩展。 

 但是有个小问题,当回文数为偶数时,没有轴,所以要分奇偶扩展两次,效率低下。

分奇偶扩展 :

class Solution {
    public String longestPalindrome(String s) {
    	int len = s.length();
    	if(len<1) {
    		return s;
    	}
    	int start=0,end=0;
    	for(int i=0;i<len;i++) {
    		int len1 = expand(s, i, i);
    		int len2 = expand(s, i, i+1);
    		int L = Math.max(len1, len2);
    		if(L>end-start) {
    			start = i - (L-1)/2;
    			end = i + L/2;
    		}
    	}
    	return s.substring(start, end+1);
    }
    public int expand(String s,int left,int right) {
    	while(left >= 0 && right < s.length() && s.charAt(left)==s.charAt(right)) {
    		left--;
    		right++;
    	}
    	return right - left - 1;
    }
}

 

 对原字符串略加修改:cbbd 变为  #c#b#b#d#,那么任何一个回文串的中心轴都有字符了

并且修改后的回文串有如下特性:1. 回文串的开始和结尾一定是 # ;

2.每个非 # 字符的下标除二得该字符在原字符串中的下标;

3.回文直径/2 == 回文半径 - 1 == 原串的回文子串的长度

(不考虑奇偶)只需一次扩展:

class Solution {
	int len,Len;
	String str;
	public String preProcess(String s) {
		String str = "#";
		for(int i=0;i<len;i++) {
			str += s.charAt(i) + "#";
		}
		return str;
	}
    public String longestPalindrome(String s) {
    	len = s.length();
    	if(len<1) {
    		return s;
    	}
    	str = preProcess(s);
    	Len = str.length();
    	int start=0,end=0;
    	for(int i=0;i<Len;i++) {
    		int len1 = expand(s, i, i);
    		if(len1>end-start) {
    			start = i - (len1-1)/2;
    			end = i + len1/2;
    		}
    	}
    	return s.substring((start+1)/2, (end-1)/2+1);
    }
    public int expand(String s,int left,int right) {
    	while(left >= 0 && right < Len && str.charAt(left)==str.charAt(right)) {
    		left--;
    		right++;
    	}
    	return right - left - 1;
    }
}

二.动态规划法

转移方程:每个最长回文子串都可以追朔到s[i] = s[i] 或者 s[i] = s[i+1] 

我们用一个数组来记录两个字符之间是否能形成回文,boolean[][] dp = new boolean[len][len];

算法核心:

完整代码:

class Solution {
    public String longestPalindrome(String s) {
    	int len = s.length();
    	if(len<2) {
    		return s;
    	}
    	boolean[][] dp = new boolean[len][len];
    	int maxLen = 1,begin=0;         //回文串最大长度,回文串起始点
    	for(int i=0;i<len;i++) {
    		dp[i][i] = true;
    	}
    	//L指当前子串长度(步长),最短为2,最长为s.length()
    	for(int L=2;L<=len;L++) {
    		for(int i=0;i<len;i++) {
    			int j = i+L-1;      //右端点j
    			if(j>=len) {        //防止右端点超出范围
    				break;
    			}
    			if(s.charAt(i)!=s.charAt(j)) {   //不相等则直接false
    				dp[i][j] = false;
    			}else {                          //相等只需判断内部子串是否回文
    				if(j-i<3) {
    					dp[i][j] = true;         //当子串长度小于4必定回文
    				}else {
    					dp[i][j] = dp[i+1][j-1];   //转移方程回溯
    				}
    			}
    			if(dp[i][j]&&j-i+1>maxLen) {
    				maxLen = j-i+1;
    				begin = i;
    			}
    		}
    	}
    	return s.substring(begin, begin+maxLen);
    }
}

 三.Manacher算法(马拉车)

1.明确几个概念:

回文半径,回文直径,中心点:例:#a#b#a#     回文半径为4,回文直径为7,中心点为b;

最右回文右边界:例:#a#c#a#v#a#b#a#

2.情况分类:

 

 3.完整代码:

class Solution {
	public static final int Min = -9999999;
	int len;
	public String preProcess(String s) {
		String str = "#";
		for(int i=0;i<len;i++) {
			str += s.charAt(i) + "#";
		}
		return str;
	}
    public String longestPalindrome(String s) {
    	int max = Min,begin=-1;
    	len = s.length();
    	if(len<1) {
    		return s;
    	}
    	String string = preProcess(s);
    	int L = string.length();
    	int[] P = new int[L];       //用来记录每个中心点对应的回文半径
    	int R=-1,center=-1;           //R表示扩成功位置的下一个位置
    	for(int i=0;i<L;i++) {
    		//前者i在R内,三种小情况i*回文半径在LR中,则取P[2*center - i],不在取R-i,
    		//压边只可能扩大,扩大情况后续代码考虑
    		P[i] = R>i ? Math.min(P[2*center - i], R-i):1;
    		//扩展代码
    		while(i + P[i] < L&&i - P[i] > -1) {
    			if(string.charAt(i + P[i])==string.charAt(i - P[i])) {
    				P[i]++;
    			}else {
    				break;
    			}
    		}
    		if(i + P[i] > R) {           //越界的话,更新center和R
    			center = i;
    			R = i+P[i];
    		}
    		if(P[i] > max) {
    			max = P[i];
    			begin = i - P[i] + 1;     //begin为修改后的串的回文子串的第一个字符的下标
                                         //(begin+1)/2为第一个字符在原串中的下标
    		}
    	}
    	return s.substring((begin+1)/2, (begin+1)/2 + max - 1);
    }
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值