Python世界:力扣题解1712:将数组分成三个子数组的方案数,中等
- 任务背景
- 思路分析
- 代码实现
- 测试套件
- 本文小结
任务背景
问题来自力扣题目1712. Ways to Split Array Into Three Subarrays,大意如下:
A split of an integer array is good if:
- The array is split into three non-empty contiguous subarrays - named
left,mid,rightrespectively from left to right.- The sum of the elements in
leftis less than or equal to the sum of the elements inmid, and the sum of the elements inmidis less than or equal to the sum of the elements inright.Given
nums, an array of non-negative integers, return the number of good ways to splitnums. As the number may be too large, return it modulo10^9 + 7.
翻译下,需求是:对给定无序数组划分成三组子数组,划分后要求左、中、右数组元素和递增,返回可划分的方法总数,若不可划分,则返回-1.
思路分析
第一个坑,读题失误,不是元素个数和递增,而是元素之和要递增。下面为错误做法,埋坑警戒。
import mathclass Solution(object):def waysToSplit(self, nums):""":type nums: List[int]:rtype: int"""res = 0nums_len = len(nums)ways_total = 0left_max = nums_len // 3 + 1for l in range(1, left_max):mid_max = (nums_len - l) // 2 + 1for m in range(l, mid_max):r = nums_len - l - mif (l <= m) and (m <= r) and (l + m + r == nums_len):ways_total += 1res = ways_totalreturn res
搞清楚真正意图后,思路如下:
- 先获取一个同样大小sum_arr数组,累加nums的元素和,构造有序数组,创造二分条件
- 对sum_arr数组做两个划分,即插两个板子,先定左板依次向右遍历
- 再浮动右板,找到第一个大于左板划分子数组mid,即找数组左上界low
- 再移动右板,找到第一个超过右板划分子数组right,即找数组右上界high
- 区间长度len=high-low,即为该定左板的可划分总数
- 依次移动左板,重复此步骤,累加len得到总的split_ways
代码实现

# nums,升序;找到第一个大于target值的索引
def get_array_left_bound(nums, l, h, t):# range: [low, high)target = tlow = lhigh = hfound_flag = Falsewhile (low < high):mid = low + (high - low) // 2if (nums[mid] >= 2*target):high = midfound_flag = Trueelse:low = mid + 1if found_flag:return lowreturn -1# nums,升序;找到最后一个大于target值的索引
def get_array_right_bound(nums, l, h, t):# range: [low, high)tar = tlow = lhigh = h# range: [low, high)found_flag = Falsewhile (low < high):mid = low + (high - low) // 2acc = nums[mid] - nums[l]left = acc + tarright = nums[h - 1] - nums[mid]if (left > right): # 不满足预期,缩减右界high = midelse: # 若 left <= rightlow = mid + 1found_flag = Trueif low > h - 1:low = h - 1if found_flag:return low # 返回low-1则表示right右界,能取到;返回low,则表示右界临界值,无法取。return -1def get_array_sum(nums):nums_len = len(nums)nums_sum = []val_sum = 0for i in range(nums_len):val_sum += nums[i]nums_sum.append(val_sum)# print(nums_sum)return nums_sumdef get_ways(nums, l):h = len(nums)t = nums[l] # left res, 0-lstart = get_array_left_bound(nums, l + 1, h, t)# start最大只能为倒数第二个索引,要确保最后至少有一个子数组if (start < 1 or start + 1 >= h):return 0t = nums[start] - nums[l] # mid res, [l+1, start]end = get_array_right_bound(nums, start, h, t)if (end <= start): # start最远能偏移的边界return 0ways = end - startreturn ways# sum + binary
class Solution(object):def waysToSplit(self, nums):""":type nums: List[int]:rtype: int"""res = 0nums_len = len(nums)nums_sum = get_array_sum(nums)# 左右边界挡板ways_total = 0left_max = nums_len - 1for l in range(0, left_max):if (2*nums_sum[l] > nums_sum[nums_len - 1]):breakways_total += get_ways(nums_sum, l)res = ways_total % 1_000_000_007return res
这里也埋了个坑,最开始94行没有注意处理大数取余10^9+7,导致[0, 0, 0, 0, 0, …]大数用例一直无法通过。
测试套件
单例测试demo:
# one case test
nums = [1,1,1]
nums = [1,2,2,2,5,0]
nums = [3,2,1]
nums = [0,0,0,0]
nums = [0, 0, 0, 0] # 3
nums = [0, 0, 0, 0, 0] # 6sol = Solution()
res = sol.waysToSplit(nums)
print(res)
套件测试demo:
# 导入单元测试
import unittestdef test_base(self, nums, ret):sol = Solution()res = sol.waysToSplit(nums)self.assertEqual(res, ret)# 编写测试套
class TestSol(unittest.TestCase):def test_special1(self):nums = [1,1,1]ret = 1test_base(self, nums, ret)def test_special2(self):nums = [0,0,0]ret = 1test_base(self, nums, ret)def test_special3(self):nums = [0,0,0,0]ret = 3test_base(self, nums, ret)def test_special4(self):nums = [0,0,0,0,0]ret = 6test_base(self, nums, ret)def test_common1(self):nums = [1,2,2,2,5,0]ret = 3test_base(self, nums, ret)def test_common2(self):nums = [3,2,1]ret = 0test_base(self, nums, ret)def test_common3(self):nums = [1,2,2,2,5,0]ret = 3test_base(self, nums, ret)# 含测试套版本主调
if __name__ == '__main__':print('start!')unittest.main() # 启动单元测试print('done!')
本文小结
此题关键点在于二分法左右边界的获取,需要对边界条件有很熟练的处理,同时注意题意处理和特殊用例,如全零大数组处理,可用排列组合Cm2公式来处理,获取理论值。
- 参考实现1:https://leetcode.com/problems/ways-to-split-array-into-three-subarrays/solutions/999157/python3-binary-search-2-pointer/
- 参考实现2:https://blog.csdn.net/weixin_59916649/article/details/125187567