力扣之最长回文子串(题号5)

4 篇文章 0 订阅
3 篇文章 0 订阅

题目叙述

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

示例 1:

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

输入: “cbbd”
输出: “bb”

题解

方法一:暴力法
截取出输入字符串的所有子串,并判断是否是回文子串,在找到最大的那个

private static String f(String s)
	{
		String max = "";
		for(int i = 0; i < s.length(); i++)
		{
			for(int j = i + 1; j < s.length() + 1; j++)
			{
				String s_son = s.substring(i, j);
				if(check(s_son))
				{
					if(s_son.length() > max.length())
						max = s_son;
				}
			}
		}
		return max;
	}
	
	private static boolean check(String s_son)
	{
		int left = 0;
		int right = s_son.length() - 1;
		while(left < right)
		{
			if(s_son.substring(left, left + 1).equals(s_son.subSequence(right, right + 1)))
			{
				left++;
				right--;
			}
			else
				return false;
		}
		return true;
	}

复杂度分析:
1.时间复杂度:O(n^3)
2.空间复杂度:O(1)

方法二:从中心向两边发散查找
假设一个字符串str是回文串,那么它一定是左右对称的,所以该字符串以某个字符str[mid]为中心的前缀和后缀一定是相同的。例如,对于回文串"aba",以’b’为中心,它的前缀和后缀都是’a’。故,可以枚举字符串的每个字符作为回文串的中心位置,左右扩展得到回文串,然后比较得到最长的长度。

private static String f2(String s)
	{
		int maxLen = 1;
		int start = 0;
		
		//求奇回文子串
		for(int i = 0; i < s.length() + 1; i++)
		{
			int left = i - 1;
			int right = i + 1;
			while(left >= 0 && right <s.length() 
					&& s.substring(left, left + 1).equals(s.substring(right, right + 1)))
			{
				if(right - left + 1 > maxLen)
				{
					maxLen = right - left + 1;
					start = left;
				}
				left--;
				right++;
			}
		}
		
		//求偶回文子串
		for(int i = 0; i < s.length(); i++)
        {
			int left = i;
			int right = i + 1;
			while(left >= 0 && right <s.length()
					&& s.substring(left, left + 1).equals(s.substring(right, right + 1)))
			{
				if(right - left + 1 > maxLen)
				{
					maxLen = right - left + 1;
					start = left;
				}
				left--;
				right++;
			}
		}
        if(s.equals(""))
            return "";
        else
			return s.substring(start, maxLen + start);
	}

复杂度分析:
1.时间复杂度:O(n^2)
2.空间复杂度:O(1)

方法三:动态规划
对于字符串s,假设dp[i,j]=1表示str[i…j]是回文子串,那个必定存在dp[i+1,j-1]=1。这样最长回文子串就能分解成一系列子问题,可以利用动态规划求解了。

private static String f3(String s)
	{
		int start = 0;
		int maxLen = 1;
		int [][] dp = new int[s.length()][s.length()];
		
		for(int i = 0; i < s.length(); i++)
		{
			dp[i][i] = 1;
			if(i < s.length() - 1)
			{
				if(s.substring(i, i + 1).equals(s.substring(i + 1, i + 2)))
				{
					dp[i][i+1] = 1;
					start = i;
					maxLen = 2;
				}
			}
		}
		
		for(int len = 3; len <= s.length(); len++)
		{
			for(int i = 0; len + i - 1 < s.length(); i++)
			{
				int j = len + i - 1;
				if(s.substring(i, i + 1).equals(s.substring(j, j + 1)) && dp[i + 1][j - 1] == 1)
				{
					dp[i][j] = 1;
					start = i;
					maxLen = len;
				}
			}
		}
		if(s.length() == 1 || s.equals(""))
			return s;
		else
			return s.substring(start, maxLen + start);
	}

复杂度分析:
1.时间复杂度:O(n^2)
2.空间复杂度:O(n^2)

方法四:Manacher算法
1.对字符串s进行预处理,在每个字符的两端插入一个相同的不会再s中出现的字符,得到一个新的字符串s_new。
2.对字符串s_new每个字符遍历,以每个字符为中心向两端查找最大的回文子串。
(1)假设计算到位置i 的字符charArr[i], 在i之前位置的计算过程中, 都会不断地更新pR 和index的值,即位置i 之前的index 这个回文中心扩出了一个最右的回文边界pR。
(2)如果pR-1 位置没有包住当前的i 位置。比如“#c#a#b#a#c#”,计算到charArr[1]==’c’时,pR 为1。也就是说,右边界在1 位置,1 位置为最右回文半径即将到达但还没有达到的位置, 所以当前的pR-1 位置没有包住当前的i 位置。此时和普通做法一样,从i 位置字符开始,向左右两侧扩出去检查, 此时的“ 扩” 过程没有获得加速。
(3) 如果pR -1 位置包住当前的i 位置。比如“#c#a#b#a#c#”, 计算到charArr [6 … 10] 时,pR 都为11,此时pR-1 包住了位置6-10。这种情况下,检查过程是可以获得优化的,这也是manacher 算法的核心内容。(百度百科)
以下代码来自百度百科

 if (str == null || str.length() == 0) {
    return null;
    }char[] charArr = manacherString(str);
 
    int[] pArr = new int[charArr.length];
    int index=-1;
    int pR=-1;
    int maxContainsEnd = -1;
    for (int i = 0; i ! = charArr.length; i++) {
    pArr [i] = pR > i ? Math.min (pArr [2 *index - i], pR - i) : 1;
    while (i + pArr[i] < charArr.length && i- pArr[i] > -1) {
    if (charArr [i + pArr [i]] == charArr[i - pArr[i]])
    pArr[i]++;
    else {
    break;
    }
    }if (i + pArr[i] > pR) {pR = i + pArr[i];
    index = i;
    }if (pR == charArr.length) {
    maxContainsEnd = pArr[i];
    break;
    }
    }
    char [] res = new char [str.length () -maxContainsEnd + 1];
    for (int i = 0; i < res.length; i++) {
    res[res.length - 1 - i] = charArr [i * 2+ 1];
    } return String.valueOf(res);

复杂度分析:
1.时间复杂度:O(n)
2.空间复杂度:O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值