1.最长回文子串
给你一个字符串 s
,找到 s
中最长的回文子串。
思路
1、中心扩散法。
最开始尝试的两边向中间扩散但是好像不行。从中间向两边可行。
每个位置都做一次中心,然后看两边最远能扩散到哪里。注意边界条件。还要注意可能是奇数个也可能是偶数个。所以中心点可能是1个也可能是2个。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
left = 0
l = len(s)
ans = s[0]
mid = 0
while mid<l:
if mid-1>=0 and mid+1<l and s[mid-1] == s[mid+1]:
left = mid-1
right = mid+1
while left-1>=0 and right+1<l and s[left-1]==s[right+1]:
left-=1
right+=1
if len(s[left:right+1])>len(ans):
ans = s[left:right+1]
if mid+1<l and s[mid] == s[mid+1] or mid-1>=0 and s[mid] == s[mid-1]:
if mid+1<l and s[mid] == s[mid+1]:
left = mid
right = mid+1
if mid-1>=0 and s[mid] == s[mid-1]:
left = mid-1
right = mid
while left-1>=0 and right+1<l and s[left-1] == s[right+1]:
left-=1
right+=1
if len(s[left:right+1])>len(ans):
ans = s[left:right+1]
mid+=1
return ans
2、动态规划法
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1],表示从下标i到下标j是否为回文子串。
或者可以用最大公众子串法(求s和s[::-1]的最大公共子串,然后还要看一下子串位置再翻转前是否对应)最大公共子串也可以用动态规划求。arr [ i ][ j ] = arr [ i - 1 ][ j - 1] + 1 。表示从i开始到j结束的最大公共子串长度。
64.最小路径和
给定一个包含非负整数的 m x n
网格 grid
,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
思路:
1、动态规划
由于只能往右或往下走,所以简单。
dp[i][j]数组代表从左上角走到i,j位置的最短路径。
状态转移方程:dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
l = len(grid)
w = len(grid[0])
dp = [[0]*w for i in range(l)]
dp[0][0] = grid[0][0]
for i in range(l):#第一行先填上
for j in range(w):#第2行再填上
if 0<=i-1 and j-1>=0:
dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]
elif 0<=i-1:
dp[i][j] = dp[i-1][j]+grid[i][j]
elif j-1>=0:
dp[i][j] = dp[i][j-1]+grid[i][j]
else:
dp[i][j] = dp[i][j]
return dp[l-1][w-1]
3.三角形最小路径和
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
思路:
1.动态规划O(n^2)空间复杂度
用1个和输入数组一样大的数组记录上一层的最短路径长度,当前层所有值的最短路径长度就等于最小的邻接上层长度加上当前值。
即状态转移方程为:ans[i][j] = min(ans[i-1][j-1],ans[i-1][j]) + triangle[i][j]
对于每一行的第0个值和最后一个值特殊处理一下。(只有一种选择)
class Solution(object):
def minimumTotal(self, triangle):
"""
:type triangle: List[List[int]]
:rtype: int
"""
l = len(triangle)
ans = triangle
for i in range(1,l):
w = len(triangle[i])
for j in range(w):
if j == 0: #每一行的第0个结点特殊处理
ans[i][j] = ans[i-1][j] + triangle[i][j]
elif j == w-1: #每一行的最后1个结点特殊处理
ans[i][j] = ans[i-1][j-1] + triangle[i][j]
else: #其余的常规情况统一处理
ans[i][j] = min(ans[i-1][j-1],ans[i-1][j]) + triangle[i][j]
return min(ans[l-1])
2.动态规划O(n)空间复杂度
O(n)空间复杂度:因为每次只会用到上一层的值,所以不用保存所有层的值,仅保存上一层的值即可,所以可以只用O(n)空间复杂度。
#用了两个数组来存储,O(2n) = O(n)
class Solution(object):
def minimumTotal(self, triangle):
"""
:type triangle: List[List[int]]
:rtype: int
"""
l = len(triangle)
m = len(triangle[l-1])
ans = [0]*m
tmp = [0]*m
ans[0] = triangle[0][0]
for i in range(1,l):
w = len(triangle[i])
tmp[0] = ans[0] + triangle[i][0]
tmp[w-1] = ans[w-2] + triangle[i][w-1]
for j in range(1,w-1):
tmp[j] = min(ans[j-1],ans[j]) + triangle[i][j]
ans = tmp[:]
return min(ans)
仅用一个数组存储也可,仅需把for j in range(1,w-1)这里的遍历改为逆序即可。for j in range(w-1,1,-1),这样不会把前面的值要用的覆盖掉。
(从前往后遍历会覆盖掉,因为下一行总是比上一行多一个值,而从后往前遍历的时候,覆盖掉的值往后移了一位,就不会影响到前面要用的值了。)
本道题也可以自下向上的动态规划,这样最后的dp[0]就是要返回的值。