LeetCode(5):最长回文子串 Longest Palindromic Substring + Manacher算法(Java)

234 篇文章 1 订阅
177 篇文章 0 订阅

2019.4.14 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

Palindromic Substring, 最长回文子串问题。

“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。

主要有两种方法:

1.中心拓展法

回文子串从中心展开,可能奇偶有两种情况。这样的回文中心一共可能有2n−1个这样的中心。从中心元素开始依次检查对称关系是否成立。每当回文子串长度超过当前最大长度时停止更新。

2.Manacher算法

Manacher算法说明参考链接1

Manacher算法说明参考链接2

Manacher算法的几个定义:

1.Manacher数组T

为了将奇偶回文中心等同考虑,可以在每一个字符两侧加上一个符号“#”,构建成一个特定的Manacher数组。比如输入的字符为acbbcbds,用“#”字符处理之后的新字符串就是#a#c#b#b#c#b#d#s#。

2.回文半径数组radius

回文半径数组radius是用来记录以每个位置的字符为回文中心求出的回文半径长度,包括每一个#。例如#a#c#b#b#c#b#d#s#的回文半径数组为[1, 2, 1, 2, 1, 2, 5, 2, 1, 4, 1, 2, 1, 2, 1, 2, 1]。

3.对称中心C与最右回文边界R

一个位置最右回文右边界指的是这个位置及之前的位置的回文子串,所到达的最右边的地方。例如Manacher数组#a#b#a#b#c#b#d#s#,当以idx=3的第一个b为中心时,R=6,最右回文边界R落在idx=6的"#"上。当以idx=4的第一个b为中心时,最右回文边界没有更新,仍然为6。

4.当前位置p,p以C为中心的对称镜像p’,以C为中心的回文左边界cL,以p’为中心的回文左边界pL。

Manacher算法的流程:

首先从左往右依次计算radius[p],当计算radius[p]时,radius[p’](0 <= j < i)已经计算完毕。分两种情况:

p <= R

如果radius[p’] < R-p,说明以p’为中心的回文串一定在以C为中心的回文串的内部,且p’和p关于位置C对称,由回文串的定义可知,一个回文串反过来还是一个回文串,所以以p为中心的回文串的长度至少和以p’为中心的回文串一样,即radius[p] >= radius[p’]。因为radius[p’] < R - p,所以说p + radius[p’] < R。由对称性可知radius[p] = Len[p’]。

如果radius[p’] >= R-p,由对称性,说明以p为中心的回文串可能会延伸到R之外,而大于R的部分我们还没有进行匹配,所以要从R+1位置开始一个一个进行匹配,直到发生失配,从而更新R和对应的C以及radius[p]。

p > R

如果p比R还要大,说明对于中点为p的回文串还一点都没有匹配,这个时候,就只能老老实实地一个一个匹配了,匹配完成后要更新R的位置和对应的C以及radius[i]。


传送门:最长回文子串

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:
输入: "cbbd"
输出: "bb"


/**
 * 
 * @author ChopinXBP
 * Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
 * Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
 * 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
 * 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
 *
 */

public class LongestPalindromicSubstring {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(longestPalindrome("cabad"));
		System.out.println(longestPalindrome("accbcd"));
		System.out.println(longestPalindrome("eababag"));
		System.out.println(longestPalindrome("abcdag"));
		System.out.println(longestPalindrome("babadada"));
	}

	//中心拓展法//
	
	//中心拓展法1:时间复杂度:O(n^2),空间复杂度:O(1)
	//回文中心的两侧互为镜像,因此可以从它的中心展开,并且只有2n−1个这样的中心(分对称轴为单个元素和对称轴为两个元素两种情况)。
	public static int left, maxlen;
	
    public static String longestPalindrome(String s) {
        if(s == null || s.length() < 2)return s;
        
        left = 0;
        maxlen = 1;
        //分对称轴为单个元素和对称轴为两个元素两种情况
        for(int i = 0; i < s.length(); i++) {
        	findMaxPalindrome(s, i, i);
        	findMaxPalindrome(s, i, i + 1);
        }
        
        return s.substring(left, left + maxlen);
    }	
    
    public static void findMaxPalindrome(String s, int latter, int former) {
    	//从中心元素开始依次检查对称关系是否成立
    	while(latter >= 0 && former < s.length() && s.charAt(latter) == s.charAt(former)) {
    		latter--;
    		former++;
    	}
    	//回文子串长度超过当前最大长度,则更新起止坐标
    	if(former - latter - 1 > maxlen) {
    		left = latter + 1;
    		maxlen = former - latter - 1;
    	}
    }
    
    //中心拓展法2(无全局变量)
	public static String longestPalindrome1(String s) {
		if (s == null || s.length() < 1)
			return "";
		int start = 0, end = 0;
		for (int i = 0; i < s.length(); i++) {
			int len1 = expandAroundCenter(s, i, i);
			int len2 = expandAroundCenter(s, i, i + 1);
			int len = Math.max(len1, len2);
			if (len > end - start) {
				start = i - (len - 1) / 2;
				end = i + len / 2;
			}
		}
		return s.substring(start, end + 1);
	}

	private static int expandAroundCenter(String s, int left, int right) {
		int L = left, R = right;
		while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
			L--;
			R++;
		}
		return R - L - 1;
	}
	
	Manacher算法/
	//https://www.jianshu.com/p/116aa58b7d81
	//字符串转换:包含头尾的所有字符间隔插入‘#’
	public static char[] manacherString(String str) {
		StringBuilder newstr = new StringBuilder();
		for (int i = 0; i < str.length(); i++) {
			newstr.append("#");
			newstr.append(str.charAt(i));
		}
		newstr.append("#");
		return newstr.toString().toCharArray();
	}

	public static int longestPalindrome2(String str) {
		if (str == null || str.length() < 1) {
			return 0;
		}

		char[] charArr = manacherString(str);
		int[] radius = new int[charArr.length];
		int R = -1;
		int C = -1;
		int max = Integer.MIN_VALUE;
		for (int p = 0; p < radius.length; p++) {
			int p1 = 2 * C - p;
			radius[p] = R > p ? Math.min(radius[p1], R - p + 1) : 1;
			while (p + radius[p] < charArr.length && p - radius[p] > -1) {
				if (charArr[p - radius[p]] == charArr[p + radius[p]]) {
					radius[p]++;
				} else {
					break;
				}
			}
			if (p + radius[p] > R) {
				R = p + radius[p] - 1;
				C = p;
			}
			max = Math.max(max, radius[p]);
		}
		return max - 1;
	}
}



#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值