题目链接 | 解题思路
因为是独立的四个数组,所以不用去重,而且我们只在乎所有数的和。所以,本质上是把第一、二个数组看作一个数组,第三、四个数组看作一个数组,从而把暴力算法的 O ( n 4 ) O(n^4) O(n4) 降低成 O ( n 2 ) O(n^2) O(n2) 。
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
sum_12 = dict()
for a in nums1:
for b in nums2:
if a+b not in sum_12:
sum_12[a+b] = 1
else:
sum_12[a+b] += 1
sum_34 = dict()
for c in nums3:
for d in nums4:
if -(c+d) in sum_12:
if -(c+d) not in sum_34:
sum_34[-(c+d)] = 1
else:
sum_34[-(c+d)] += 1
count = 0
for sum in sum_34:
count += sum_12[sum] * sum_34[sum]
return count
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
sum_12 = dict()
for a in nums1:
for b in nums2:
if a+b not in sum_12:
sum_12[a+b] = 1
else:
sum_12[a+b] += 1
count = 0
for c in nums3:
for d in nums4:
if -(c+d) in sum_12:
count += sum_12[-(c+d)]
return count
from collections import defaultdict
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
record = defaultdict(lambda : 0)
count = 0
for a in nums1:
for b in nums2:
record[a+b] += 1
for c in nums3:
for d in nums4:
if record[-(c+d)]:
count += record[-(c+d)]
return count
题目链接 |
重点是只会出现小写字母,所以数值空间范围是有限的。这种明确的条件下,最好是使用数组来构建 hash table。相比之下,mapping 没有数组好。
使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
records = dict()
for letter in ransomNote:
if letter not in records:
records[letter] = 1
else:
records[letter] += 1
for letter in magazine:
if letter in records:
records[letter] -= 1
for key in records:
if records[key] > 0:
return False
return True
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
records = [0] * 26
for letter in ransomNote:
records[ord(letter) % 26] += 1
for letter in magazine:
records[ord(letter) % 26] -= 1
for i in range(len(records)):
if records[i] > 0:
return False
return True
from collections import Counter
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
return not Counter(ransomNote) - Counter(magazine)
题目链接 | 解题思路
这道题看上去和 1.两数之和 / 454.四数相加II 看上去很相似,但些许的不同导致了最终解法的不同。在两数之和中,要返回的是 index,而这道题中返回的是元素值。
更重要的是,之前的题是允许 index/value重复的,但是这次题目要求不能使用重复的 index(最终结果中每个子数组中可以有相同的 value),这点对于 hash table 是致命的,因为使用哈希法进行查询的时候一般并不在乎元素的 index,所以去重会变得非常复杂,同时也失去了使用哈希法的意义。(在自己做的时候,哈希+剪枝也还是超时了)
同时要注意题目中对重复元素的要求:
两数之和 就不能使用双指针法,因为 1.两数之和 要求返回的是索引下标, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
如果 1.两数之和 要求返回的是数值的话,就可以使用双指针法了。
需要找到符合条件的 a + b + c = 0 a+b+c=0 a+b+c=0。以下的关系中, a a a=nums[i]
, b b b=nums[left_idx]
, c c c=nums[right_idx]
i
,遍历整个数组,指代 a a a
nums[i] > 0
,由于从小到大排列,代表着肯定无法达成 a + b + c = 0 a+b+c=0 a+b+c=0,直接结束遍历nums[i] == nums[i-1]
,则当前的 a a a 已经在之前的 iteration 中被计算过了,当前循环必然会得到和之前 iteration 一模一样的结果,直接跳过当前 iteration(直到发现新的 a a a)
nums[i] == nums[i+1]
,因为后者会漏掉直接跳过一个元素第一次出现的情况i
,i
的右侧规定了 left_idx
和 right_idx
的范围,即 b b b 和 c c c 的范围
right_idx
left_idx
left_idx
直到发现一个新的 b b b,达成对于 b b b 的去重right_idx
直到发现一个新的 c c c,达成对于 c c c 的去重在以上算法中,关于i
,left_idx
, right_idx
的定义决定了 三元子数组内的元素 index 不会相同,同时完整的去重也确保了不会出现重复的三元子数组。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
results = []
for i in range(len(nums)):
# if the first value in i, left_idx, right_idx is positive, no way can achieve sum=0, for-loop ends
if nums[i] > 0:
break
# avoid the duplicates of "a": if the value of i has occurred, no need to consider it again, otherwise duplicates of "a" occur
if i > 0 and nums[i] == nums[i - 1]: # avoid duplicates
continue
# uniqueness of "a" has been guaranteed
# left_idx, right_idx denote the range of the left interval
left_idx = i + 1
right_idx = len(nums) - 1
while (left_idx < right_idx):
if nums[i] + nums[left_idx] + nums[right_idx] > 0: # need to decrease the large value
right_idx -= 1
elif nums[i] + nums[left_idx] + nums[right_idx] < 0: # need to increase the small vale
left_idx += 1
else:
results.append([nums[i], nums[left_idx], nums[right_idx]])
# meet a satisfatory case, move left_idx and right_idx specially
curr_left_val, curr_right_val = nums[left_idx], nums[right_idx]
while (nums[left_idx] == curr_left_val and left_idx < right_idx): # move left_idx to a new value
left_idx += 1
while (nums[right_idx] == curr_right_val and left_idx < right_idx): # move right_idx to a new value
right_idx -= 1
return results
题目链接 | 解题思路
思路与上一题基本相同,依然是固定两个遍历指针,在剩余区间内依靠双指针找到剩下符合条件的解。
唯一变化的是剪枝的条件发生了变化:由于不清楚 target 的正负,剪枝的条件变得更加苛刻了。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
results = []
for i in range(len(nums)):
if nums[i] > target and nums[i] > 0 and target > 0: # cut the case
break
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i+1, len(nums)):
if j < len(nums) - 1 and nums[j] == nums[j + 1]:
continue
left_idx = i + 1
right_idx = j - 1
while (left_idx < right_idx):
if nums[i] + nums[j] + nums[left_idx] + nums[right_idx] > target:
right_idx -= 1
elif nums[i] + nums[j] + nums[left_idx] + nums[right_idx] < target:
left_idx += 1
else:
results.append([nums[i], nums[j], nums[left_idx], nums[right_idx]])
curr_left_val, curr_right_val = nums[left_idx], nums[right_idx]
while (nums[left_idx] == curr_left_val and left_idx < right_idx):
left_idx += 1
while (nums[right_idx] == curr_right_val and left_idx < right_idx):
right_idx -= 1
return results