674.最长连续递增序列
题目描述
给定一个未经排序的整数数组,找到最长且连续的的递增序列。
难度:简单
示例 1: 输入:nums = [1,3,5,4,7] 输出:3 解释:最长连续递增序列是 [1,3,5], 长度为3。 尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。 示例 2: 输入:nums = [2,2,2,2,2] 输出:1 解释:最长连续递增序列是 [2], 长度为1。 提示: 1 <= nums.length <= 104 -109 <= nums[i] <= 109
解题思路
这道题是不是一眼看过去和上题非常的像,没错了,这个题目最大的不同就是连续两个字,这样就让这个问题简单很多了,因为如果要求连续的话,那么就不需要和上题一样遍历两遍数组,只需要比较前后的值是不是符合递增的关系。
- 第一步:确定动态规划状态
对于这个问题,我们的状态dp[i]也是以nums[i]这个数结尾的最长递增子序列的长度 - 第二步:写出状态转移方程
这个问题,我们需要分两种情况考虑,第一种情况是如果遍历到的数nums[i]
后面一个数不是比他大或者前一个数不是比他小,也就是所谓的不是连续的递增,那么这个数列最长连续递增序列就是他本身,也就是长度为1。
第二种情况就是如果满足有递增序列,就意味着当前状态只和前一个状态有关,dp[i]
只需要在前一个状态基础上加一就能得到当前最长连续递增序列的长度。总结起来,状态的转移方程可以写成dp[i]=dp[i-1]+1
- 第三步:考虑初始化条件
和上面最长子序列相似,这个题目的初始化状态就是一个一维的全为1的数组。 - 第四步:考虑输出状态
与上题相似,这个问题输出条件也是求dp数组中最大的数。 - 第五步:考虑是否可以优化
这个题目只需要一次遍历就能求出连续的序列,所以在时间上已经没有可以优化的余地了,空间上来看的话也是一维数组,并没有优化余地。
class Solution: def findLengthOfLCIS(self, nums) -> int: if not nums: #判断边界条件 return 0 #初始化dp数组状态 dp = [1] * len(nums) #注意需要得到前一个数,所以从1开始遍历,否则会超出范围 for i in range(1,len(nums)): if nums[i] > nums[i-1]: dp[i] = dp[i-1]+1 else: dp[i] = 1 return max(dp) # 确定输出状态
5. 最长回文子串
题目描述
难度:中等
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
解题思路
- 第一步:确定动态规划状态
与上面两题不同的是,这个题目必须用二维的dp数组来记录状态,主要原因就是子串有回文的限制。用两个指针来记录子串的位置可以很好的实现子串的回文要求,又因为最后结果需要返回的是子串,这里不同于之前题目的用dp保存长度,我们必须找到具体哪个部分符合回文子串的要求。这里插一句,其实也有求回文子串长度的题目Leetcode516. 最长回文子序列,如果有兴趣可以看一下。这里我们定义dp[i][j]
表示子串s从i到j是否为回文子串。 - 第二步:写出状态转移方程首先我们需要知道符合回文的条件:
- 字符串首尾两个字符必须相等,否则肯定不是回文。
- 当字符串首尾两个字符相等时:如果子串是回文,整体就是回文,这里就有了动态规划的思想,出现了子问题;相反,如果子串不是回文,那么整体肯定不是。
对于字符串s,s[i,j]
的子串是s[i+1,j-1]
,如果子串只有本身或者空串,那肯定是回文子串了,所以我们讨论的状态转移方程不是对于j-1-(i+1)+1<2
的情况(整理得j-i<3
),当s[i]
和s[j]
相等并且j-i<3
时,我们可以直接得出dp[i][j]
是True。 - j-i<3是关键
综上所述,可以得到状态转移方程
if s[i]==s[j]: if j-i<3: dp[i][j]=True else: dp[i][j]=dp[i+1][j-1]
- 第三步:考虑初始化条件
我们需要建立一个二维的初始状态是False的来保存状态的数组来表示dp,又因为考虑只有一个字符的时候肯定是回文串,所以dp表格的对角线dp[i][i]
肯定是True。 - 第四步:考虑输出状态
这里dp表示的是从i
到j
是否是回文子串,这样一来就告诉我们子串的起始位置和结束位置,但是由于我们需要找到最长的子串,所以我们优化一下可以只记录起始位置和当前长度(当然你要是喜欢记录终止位置和当前长度也是没问题的)
if dp[i][j]: #只要dp[i][j]成立就表示是回文子串,然后我们记录位置,返回有效答案 cur_len=j-i+1 if cur_len>max_len: max_len=cur_len start=i
- 第五步:考虑对时间,空间复杂度的优化
对于这个问题,时间和空间都可以进一步优化,对于空间方面的优化:这里采用一种叫中心扩散的方法来进行,而对于时间方面的优化,则是用了Manacher‘s Algorithm(马拉车算法)来进行优化。具体的实现可以参考动态规划、Manacher 算法
这里给出比较容易理解的经典方法的代码:
def longestPalindrome(self, s: str) -> str: length =len(s) if length <2: return s # babad # b1bad # 10bad # babad # babad # 定义状态转移矩阵 dp = [[False for _ in range(length)] for _ in range(length)] max_len = 1 start = 0 for j in range(1,length): for i in range(j): if s[i]==s[j]: if j-i <3: dp[i][j]=True else: dp[i][j] = dp[i+1][j-1] if dp[i][j]: cur_len = j-i+1 if cur_len > max_len: max_len = cur_len start = i return s[start:start+max_len]