5最长回文子串(中心扩展法、动态规划)

1、题目描述

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

2、示例 

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

3、题解

解法一:中心扩展法

  • 算法思想:以center下标作为回文子串的中心,i标记回文子串的左边,j标记回文子串的右边,初始i和j为center,通过s[i] == s[j]不断从中心center向外扩展i--,j++。时间复杂度O(n^{2})
  • 若最大回文子串长度为奇数,i和j同时从center扩展开,i=j=center
  • 若最大回文子串长度为偶数,i从center扩展开,j从center+1扩展开,i=center j=center+1

解法二:动态规划

  • 算法思想:动态规划方法是不断利用已求得的信息计算未求得的信息,在这个问题中可以理解为如果已知dp[i+1][j-1]=true&&s[i] = s[j],即从下标i+1到下标j-1为回文子串并且字符s[i]=s[j],那么dp[i][j]=true从下标i到下标j也为回文子串。
  • 先初始化数组pd,dp[i][i] = true单个字符自身属于回文串是长度最小的回文串,相连的两个字符相同s[i]=s[i+1]组成回文串
  • 然后循环判断字符串s中子串长度len从3到s_len中是否存在回文串,子串下标从0到s_len - len,如果dp[i+1][j-1]=true&&s[i] == s[j]则长度len的子串(下标从i到j)为回文串dp[i][j]=true;因为子串长度是从小到大的,所以求出小的回文子串,求大的回文子串的时候就可以利用求出小的回文子串的信息,即如果dp[i+1][j-1]=true&&s[i]=s[j],那么dp[i][j]=true。这就是这个问题的状态转移方程。
  • 动态规划问题最重要的是求出状态转移方程,需要根据问题的特性找出状态转移方程,不断利用已求得的信息计算未求得的信息。
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Solution {
public:
	string longestPalindrome(string s) {
		//简化版:动态规划+中心扩展法,dp[i][j]表示s中[i,j]范围字符是否构成回文串
		int start, max_len = 0;
		vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));	
		for(int len=1;len<=s.size();len++)
		{
			for(int i=0;i<=s.size()-len;i++)
			{
				int j=i+len-1;
				if(s[i]==s[j]&&(i==j||i+1==j||dp[i+1][j-1]))
				{
					dp[i][j]=true;
					start=i;
					max_len=len;
				}		
			}
		}
		return max_len==0?s.substr(0,1):s.substr(start,max_len);
	}
};
//中心扩展法
class Solution1 {
public:
	string longestPalindrome(string s) {
		int start, max_len = 0;  //最大回文子串开始的下标start,最大回文子串长度max_len
		int i, j, center;  //center作为回文子串的中心,i标记回文子串的左边,j标记回文子串的右边,通过s[i] == s[j]不断从中心center向外扩展i--,j++
		int len = s.length();  //len为字符串s的长度
		if (len == 1 || len == 0)  //如果字符串为空或者只有一个字符,直接返回s
			return s;
		//若最大回文子串长度为奇数,i和j同时从center扩展开
		for (center = 0; center < len; center++)
		{
			i = center;
			j = i;
			while (i >= 0 && j <= (len - 1) && s[i] == s[j])
			{
				i--;
				j++;
			}
			if ((j - i + 1) - 2 > max_len)
			{
				max_len = j - i + 1 - 2;
				start = i + 1;
			}
		}
		//若最大回文子串长度为偶数,i从center扩展开,j从center+1扩展开
		for (center = 0; center < len; center++)
		{
			i = center;
			j = i + 1;
			while (i >= 0 && j <= (len - 1) && s[i] == s[j])
			{
				i--;
				j++;
			}
			if ((j - i + 1) - 2 > max_len)
			{
				max_len = j - i + 1 - 2;
				start = i + 1;
			}
		}
		//max_len=0说明没有回文串,所以返回第一个字符就可以了
		if (max_len == 0)
			return s.substr(0, 1);
		else  //返回截取s的回文串开始下标start长度为max_len
			return s.substr(start, max_len);
	}
};
//动态规划法
class Solution2 {
public:
	string longestPalindrome(string s) {
		int start, max_len = 0;
		int i, j;  //i标记子串的起始下标,j标记子串的终止下标,子串长度len=j-i+1
		int s_len = s.length();  //s_len为字符串s的长度
		int len;
		bool dp[1000][1000] = { false };  //pd[i][j]=true表示从下标i到下标j的字符子串为回文串
		if (s_len == 1 || s_len == 0)  //如果字符串为空或者只有一个字符,直接返回s
			return s;
		//初始化pd
		for (i = 0; i < s_len - 1; i++)
		{
			dp[i][i] = true;  //单个字符属于回文串,是长度最小的回文串
			j = i + 1;
			//相连的两个字符相同,则这两个字符组成回文串
			if (s[i] == s[j])
			{
				dp[i][j] = true;
				max_len = j - i + 1;
				start = i;
			}

		}
		//循环判断字符串s中子串长度len从3到s_len中是否存在回文串,子串下标从0到s_len - len,如果dp[i+1][j-1]=true&&s[i] == s[j]则长度len的子串(下标从i到j)为回文串,dp[i][j]=true
		for (len = 3; len <= s_len; len++)
		{
			for (i = 0; i <= s_len - len; i++)
			{
				j = i + len - 1;
				//如果dp[i+1][j-1]=true && s[i]=s[j],则长度len的子串(下标从i到j)为回文串
				if (dp[i + 1][j - 1] && s[i] == s[j])
				{
					dp[i][j] = true;
					max_len = len;  //因为子串长度len从3到s_len中是否存在回文串,所以如果存在回文串,回文串的长度是越来越大的,即max_len就等于len,不用判断len是否大于max_len
					start = i;
				}
			}
		}
		//max_len=0说明没有回文串,所以返回第一个字符就可以了
		if (max_len == 0)
			return s.substr(0, 1);
		else  //返回截取s的回文串开始下标start长度为max_len
			return s.substr(start, max_len);
	}
};
int main()
{
	Solution solute;
	string s;
	s = "abbbcd";
	cout << solute.longestPalindrome(s) << endl;
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值