1、序言
生活:赚钱,赚更多钱,然后进入ICU,嗝屁。
2、题目
给你一个字符串 s
,找到 s
中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
3、思路
第一种思路我还是采用双指针的方法,第一个指针控制子串的起始位置,第二个指针控制子串的终止位置,然后循环判断是否为回文子串并更新最长的回文子串。第二种思路是动态规划,是我第一种思路的时间复杂度太高导致无法通过时才学习的新方法。
3.1、思路一代码
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n == 0:
return ''
max_str = s[0]
for i in range(n):
for j in range(n-i):
if s[i:i+j+1] == s[i:i+j+1][::-1] and len(s[i:i+j+1])>len(max_str):
max_str = s[i:i+j+1]
return max_str
思路一的粗暴方法行不通,接下来开始学习动态规划。
4、动态规划
动态规划是一种阶段性求解策略问题的数学思想。先用最简单的上台阶题分析,有n个台阶,每次只能上一个台阶或者两个台阶,问走到第n个台阶有多少种走法?
分析:当n等于4的时候,走法图形化如下。走法有三种:
(1)1-->2-->4
(2)1-->3-->4
(3)1-->2-->3-->4
当你走到第四个台阶的时候,可能情况只有两种,一种是从2台阶上来,一种是从三台阶上来,所以就分解为两个问题:走到2有几种方法?走到3有几种方法? 所以走到4的方法等于走到2的方法加上走到3的方法。
这时候先考虑递归,递归的核心是推衍和回溯,以及递归出口。针对本题的递归方程设计为: f(n) = f(n-1)+f(n-2)
def walk_method1(n):
# 递归方法
# 递归分为推演和回溯
if n == 0:
return 0
if n == 1: # 递归出口
return 1
if n == 2:
return 2
return walk_method1(n-1)+walk_method1(n-2)
但是递归的缺点是重复计算,比如走到4要计算走到2和走到3,计算走到3时又要重复计算走到2。
这时候我们考虑使用一个哈希表,用来存储重复计算节点的结果。这就是查表的备忘录算法。
def walk_method2(n):
# 备忘录方法
memo = {}
if n == 0:
return 0
if n == 1: # 递归出口
return 1
if n == 2:
return 2
if n in memo.keys():
return memo[n]
else:
value = walk_method1(n-1)+walk_method1(n-2)
memo[n] = value
return value
递归或者备忘录算法是自顶向下,然后再自底向上。这时候我们思考为啥不直接从底部直接上到顶部?动态规范的核心是:最优子结构,边界,状态转移公式。f(2)和f(3)是f(4)的最优子结构,f(1)和f(2)是问题的边界,状态转移公式是 f(n) = f(n-1)+f(n-2)。我们开始由1和2状态得到3状态,再忘记1状态,更新到2状态和3状态得到4状态就得到f(4),这就是简单的动态规划思想。
def walk_method3(n):
step1 = 1
step2 = 2
tmp = 0
for i in range(n-2):
#从第三个台阶开始计算
tmp = step1 + step2
#更新状态
step1 = step2
step2 = tmp
return tmp
让我们再来一个难一点的动态规划题目,国王和金矿:有一个国家发现了5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同。参与挖矿工人的总数是10人。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿。要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?答案参考链接https://www.sohu.com/a/149075950_684445?spm=smpc.content.share.1.1615368141569lixrfPF#comment_area,我实在是看不下去了,脑壳有点疼。
5、解题
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
dp = [[False] * n for _ in range(n)]
ans = ""
# 枚举子串的长度 l+1
for l in range(n):
# 枚举子串的起始位置 i,这样可以通过 j=i+l 得到子串的结束位置
for i in range(n):
j = i + l
if j >= len(s):
break
if l == 0:
dp[i][j] = True
elif l == 1:
dp[i][j] = (s[i] == s[j])
else:
dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
if dp[i][j] and l + 1 > len(ans):
ans = s[i:j+1]
return ans
这是其他人提交的动态规划,结果我还是超时,我放弃这这个题了。学了个寂寞。