每天的刷题记录
2020.5.21 最长回文子串
学习了动态规划(dp)算法,这个算法本质上是一个递归的算法,只要是递归就要注意递归的截止条件,动态规划中要会列状态转移方程。
本题中:寻找最长回文子串,如果要用递归的思想,则先从最简单的回文串开始考虑:
回文串length = 1时,它肯定是回文串
回文串length = 2时,要判断s[0]和s[1]是不是相等的,才能判断是否是回文串
递归:回文串长度 >2时,则先判断头尾元素是否相等,如果相等,则去掉头尾,继续判断这个子回文串
递归终止条件:去掉头尾后,长度为0
动态规划本质上就是这个递归思想,只不过递归的过程用状态转移方程来描述:
dp[i][j] = dp[i+1][j-1]
dp[i][j]就是s[i:j](左闭右闭)是否是回文串,因此要调整一下递归的终止条件,不能让dp[i+1][j-1] 变得奇奇怪怪
最后,对于每一个回文串,记录长度和index即可,本题leetcode通过。
class Solution:
def longestPalindrome( self, s: str) -> str:
max_len = 1
start = 0
length = len(s)
if length <2:
return s
dp = [[False ]*length for _ in range(length) ]
for i in range(length):
dp[i][i] = True # 第i个元素自己本身是回文数
for j in range(1,length):
for i in range(0,j):
if s[i] == s[j]:
if j - i <3 :
dp[i][j] = True
else:
dp[i][j] = dp[i+1][j-1] #状态转移方程
else:
dp[i][j] = False
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]
2020.5.22 从前序遍历,中序遍历中构造二叉树
这一题也是应用了递归的思想,对于二叉树的题,递归很方便,而能用递归的思想就是别把问题看的太复杂。比如
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
注意到前序遍历顺序“根左右”,中序遍历顺序“左根右”,因此就应该看成
前序遍历preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
这样就很清晰了,代码leetcode通过
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if not preorder or not inorder :
return None
root = TreeNode(preorder[0])
idx = inorder.index(preorder[0])
root.left = self.buildTree(preorder[1:1+idx],inorder[:idx])
root.right = self.buildTree(preorder[1+idx:],inorder[idx+1:])
return root
2020.5.23 最小覆盖子串
从这道题中学习到了一种滑动窗口的方法,也就是双指针。
原理:一开始两个指针指向头,右指针往右走,边走边检查是不是识别到了子串,也就是不断增加窗口长度的过程。当右指针走到一个位置,他是s的子串时,此时要记录头尾索引长度等信息来作为判断。之后左指针开始往右走,窗口长度开始减小,只要他还满足子串要求,则就应该继续记录信息。一直到他不是子串了,窗口就不能继续变小,而是应该增大了。这个窗口就在这滑动过程中变大变小,最短位置的信息也就在其中被记录了。
此外,子串判断这里应该是要用字典的,但是我不会,只好自己构造了一个Map类,他的作用就是一个键值对映射,用来记录元素以及元素出现次数。代码leetcode通过用例267/268,最后一个巨长通过不了,超时了
class Map:
def __init__(self):
self.val = []
self.count = []
def add(self,string):
for str1 in string:
if str1 not in self.val:
self.val.append(str1)
self.count.append(1)
else:
idx = self.val.index(str1)
self.count[idx] += 1
def is_sub(self,cutmap):
for i in self.val:
if i not in cutmap.val:
return False
tidx = self.val.index(i)
cidx = cutmap.val.index(i)
if self.count[tidx] > cutmap.count[cidx]:
return False
return True
def t_in_cut(cut,t):
if len(t) > len(cut):
return False
tmap= Map()
tmap.add(t)
cutmap = Map()
cutmap.add(cut)
if tmap.is_sub(cutmap):
return True
else:
return False
class Solution:
def minWindow(self,s: str, t: str) -> str:
left = 0
right = 1
length = float('inf')
idx_start = 0
idx_end = 0
count = 0
cut = s[left:right]
for right in range(1,len(s)+1):
cut = s[left:right]
while t_in_cut(cut,t):
cut = s[left:right]
cur_length = right - left
if cur_length < length:
length = cur_length
idx_start = left
idx_end = right
left += 1
return(s[idx_start - 1:idx_end])
2020.5.24 两数相加
看题目描述觉得很简单,实际上逻辑判断起来还是蛮头大的,也可能是我对链表运用得不熟悉吧。
我的思路是:链表中两个数相加,首先判断链表长度是不是一样的,其次判断有没有进位,也就是判断两次,有2*2 = 4 种情况。
长度一样,有进位:进位计数器置1,创造下一节点;
长度一样,无进位:进位计数器置0,如果后面还有长度一样的,创造下一节点;
长度不一样,有进位:进位计数器置1,创造下一节点;
长度不一样,无进位:进位计数器置0,如果后面还有l1或l2,创造下一节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
head = res = ListNode(0)
c = 0
#如果数位是对齐的
while l1 !=None and l2 !=None:
#判断是否进位
if l1.val +l2.val +c <=9:
res.val = l1.val +l2.val +res.val
c = 0
#没进位,再判断是否要创建新元素
if l1.next !=None and l2.next !=None:
out = res.next = ListNode(0)
res = out
#进位了肯定有新元素
else:
res.val = l1.val +l2.val -10 +res.val
out = res.next = ListNode(1)
res = out
c =1
l1,l2 = l1.next,l2.next
#如果数位没对齐
while l1 !=None:
if c == 0:
out = res.next = ListNode(l1.val)
res = out
else:
if res.val + l1.val <10:
res.val = res.val + l1.val
c = 0
else:
res.val = res.val + l1.val -10
out = res.next = ListNode(1)
res = out
c = 1
l1 = l1.next
while l2 !=None:
if c == 0:
out = res.next = ListNode(l2.val)
res = out
else:
if res.val + l2.val <10:
res.val = res.val + l2.val
c = 0
else:
res.val = res.val + l2.val -10
out = res.next = ListNode(1)
res = out
c = 1
l2 = l2.next
return head
2020.5.25 LRU缓存机制
手撸字典解决,leetcode通过
class LRUCache:
def __init__(self, capacity: int):
self.keylist = []
self.vallist = []
self.capacity = capacity
def get(self, key: int) -> int:
if key in self.keylist:
idx = self.keylist.index(key)
self.keylist.append(self.keylist[idx])
self.vallist.append(self.vallist[idx])
del self.keylist[idx]
del self.vallist[idx]
return self.vallist[len(self.vallist)-1]
else:
return -1
def put(self, key: int, value: int) -> None:
if key in self.keylist:
idx = self.keylist.index(key)
self.vallist[idx] = value
self.keylist.append(self.keylist[idx])
self.vallist.append(self.vallist[idx])
del self.keylist[idx]
del self.vallist[idx]
else:
if len(self.keylist) >= self.capacity:
del self.keylist[0]
del self.vallist[0]
self.keylist.append(key)
self.vallist.append(value)
2020.5.27 可被K整除的子数组
一开始的直接思路是暴力求解,用两层for循环遍历所有子数组,用sum求数组的和,这样时间复杂度就是O(n3),直接不通过了
看了题解,学到了一种用哈希表解题的,时间复杂度居然只有O(n),不过感觉还不是太理解,先贴代码,看看过几天还记不记得
class Solution:
def subarraysDivByK(self, A: List[int], K: int) -> int:
#暴力遍历所有可能的子数组,不通过
"""
count = 0
if A
for i in range(len(A)):
for j in range(i+1,len(A)+1):
cut = A[i:j]
if sum(cut)%K == 0:
count +=1
return count
"""
res = {0:1}
total,out = 0,0
for val in A:
total += val
modres = total%K
same =res.get(modres,0)
out += same
res[modres] = same + 1
return out