【LeetCode 5-中等】最长回文子串(高清截图)

5. 【中等】最长回文子串

https://leetcode-cn.com/problems/longest-palindromic-substring/
给你一个字符串s,找到s中最长的回文子串。

示例 1:
	输入:s = "babad"
	输出:"bab"
	解释:"aba" 同样是符合题意的答案。
示例 2:
	输入:s = "cbbd"
	输出:"bb"
示例 3:
	输入:s = "a"
	输出:"a"
示例 4:
	输入:s = "ac"
	输出:"a"

提示:
1 <= s.length <= 1000
s仅由数字和英文字母(大写和/或小写)组成

【解】

【方法1】动态规划(DP)。


P(i, j)={█(“true” &, s[i, j]“是回文串” @“false” &, s[i, j]“不是回文串,或不合法(” i>j")" )┤
若字符串s的某个子串s[i, j]是回文串,且最邻近它两端的字符均相等,即s[i-1]==s[j+1],则字符串s[i-1, j+1]也是回文串。因此,状态转移方程
{█(P(i, j)&=P(i+1, j-1)∧s[i]==s[j]@P(i, i)&=“true” @P(i, i+1)&=s[i]==s[i+1] )┤
易得边界条件:长为1的字符串本身就是其最长回文子串;长为2的字符串,要么它本身为它的最长回文子串,要么它没有任何回文子串。
实现的时候要注意,一个字符串s的长度大于2的子串s[i, j]是否为s的最长回文子串,需要凭借之前已经判定过的长度更短的字串的判定结果来做出判定。
代码:

#include <vector>

class Solution {
public:
	string longestPalindrome(const string& s) {
		if (s.size() < 2) return s;							// 长度为 1 的字符串本身就是其最长回文子串
		const size_t n = s.size();							// 字符串 s 的长度记为 n
		size_t lmax = 1;									// 最大长度
		size_t lp_begin = 0;								// 一个最长回文子串的起始位置
		vector<vector<bool>> p(n, vector<bool>(n));			// p[i][j] 表示:字符串 s[i, j] 是否为回文串
		for (size_t i = 0; i < n; ++i) p[i][i] = true;		// 长为 1 的全部子串都是回文串
		for (size_t l = 2; l <= n; ++l) {					// 枚举长为 l = 2, 3, ..., n 的子串
			for (size_t i = 0, j; i < n; ++i) {				// 枚举长为 l 的子串的全部可能出现的位置
				j = l + i - 1;
				if (j >= n) break;							// 下标越界,字符串非法
				if (s[i] != s[j]) p[i][j] = false;
				else {
					if (j - i < 3) p[i][j] = true;			// 正在考察的字符串 s[i, j] 长为 1 或 2,且 s[i] == s[j],它应当是回文串
					else p[i][j] = p[i + 1][j - 1];			// 根据之前枚举过的长度更短的字符串,判定本字符串 s[i, j] 是否为回文串
				}
				if (p[i][j] && j - i + 1 > lmax) {			// 更新最长回文子串的统计数据
					lmax = j - i + 1;
					lp_begin = i;
				}
			}
		}
		return s.substr(lp_begin, lmax);					// 给出一个解
	}
};
用时:584 ms,短于26.94%的用户;内存占用:29.2 MB,少于55.93%的用户。

【方法2】从回文中心向两端扩展

枚举全部长度为1或2的子串,不断考察分别与子串两端相邻的2个字符。若添加这2个字符后,新子串仍为回文串,则继续如此添加下去,直到不能再添加为止。
代码:

#include <vector>

class Solution {
	pair<ptrdiff_t, ptrdiff_t> ExpandFromPalindromicCenter(const string& S, ptrdiff_t L, ptrdiff_t R) {
		while (L >= 0 and R < S.size() and S[L] == S[R]) { --L; ++R; }
		return { L + 1, R - 1 };
	}
public:
	string longestPalindrome(const string& s) {
		ptrdiff_t L = 0, R = 0;
		for (ptrdiff_t i = 0; i < s.size(); ++i) {
			auto [L1, R1] = ExpandFromPalindromicCenter(s, i, i);			// 回文中心为一个字符的情况
			auto [L2, R2] = ExpandFromPalindromicCenter(s, i, i + 1);		// 回文中心为两个字符的情况
			if (R1 - L1 > R - L) { L = L1; R = R1; }
			if (R2 - L2 > R - L) { L = L2; R = R2; }
		}
		return s.substr(L, R - L + 1);
	}
};
用时:16 ms,优于92.08%的用户;内存占用:6.9 MB,优于95.08%的用户。

【方法3】Manacher算法

(待填坑)

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值