子串/子序列最值

Subarray最值简介

在原有数组(或字符串)中寻找子串(或子字符串)的最值,暴力法一般是 O(n³)的时间复杂度。因为枚举首尾需要O(n²),而判断断子串符合要求另需要O(n)

要把总时间复杂度O(n³)降下来到有O(n²),O(nlogn)甚至O(n),有这么几种方法可以加速:

  • 前缀和

  • 双指针(滑动窗口)

  • 动态规划

  • 二分查找

  • 中心扩展

  • 单调栈

(1) 前缀和

用数组或者哈希表来记录前 ii 个数字的连续和。

参考模版:

presum = [0 for _ in range(len(n)+1)] # sum of nums[:i]
for i in range(n):
    presum[i+1] = presum[i] + nums[i]

前缀和可以用来加速求解区间和。定义前缀和 Si=n0,1,...,i1S_i = \sum n_{0, 1, ..., i-1} ,那么

ni...j=sj+1si\sum n_{i...j} = s_{j+1} - s_{i}

(2) 双指针

参考模版:

这个方法很实用于解决这样一类问题:符合某某要求的最长子串。有一些题目看上去不像子串最值,但是通过转换就变成了““题,可以用滑动窗口求解。

很重要的一点就是:如何定义"符合要求"。下面几个例子感受一下:

简单:

中等:

(3) 动态规划

情况一:一次遍历,用O(1)的空间就能解决。相当于运用了滚动数组的动态规划,因为转移方程中f[i]只依赖于f[i-1]例子有:

情况二:二维DP或区间DP。具体参见动态规划章节。

(4) 二分查找

对“符合某某要求的xx最大值子串“,二分查找的思路一般是通过猜xx最大值->验证可行性->修正的过程。是二分查找中比较难的类型。有的时候要借助于前缀和来帮助快算区间和。

(5) 中心扩展/单调栈

参考代码:

分治:从中间向外贪心展开,左右分别操作,最后合并结果。虽然能得到正确答案,然而时间复杂度为O(nlogn)

Subsequence最值

枚举长度为n的序列的所有Subsequence有2ⁿ个,所以枚举肯定会超时。subsequence的最值一般通过O(n²)的动态规划求解。

Last updated

Was this helpful?