这类问题都是求一个满足某种条件的连续的段,相对于子序列。字符串和数组的主要区别是,前者因为由字符而不是数值组成,一般不会有最大值,最小值,求和这种问题。对于字符串的子串问题,一般条件是子串符合某种pattern(回文、有序)或者满足某种统计特征(子串内无重复字符,最多重复2次,包含了给定字符集的所有字符,字符个数均等)
子数组问题包含了子串问题,但是可以用子数组的一些数值计算特征值(比如sum, max)作为条件,最大子数组和, 最短的和为target的子数组
最基本的暴力枚举框架是O(n^3)的,枚举所有子数组需要O(n^2),然后判断子数组是否满足条件(计算特征值,check pattern),一般要check子数组内所有元素,是O(n)的,整体就是O(n^3)。
暴力枚举的框架就是由 枚举+计算子数组特征值两部分组成,都有可能优化,计算特征值的优化是累加利用之前的结果,比如 A[i...j]上的sum或 max 可以由A[i...j-1]上的结果O(1)累加而来,不用遍历子数组。枚举的可能的优化是滑动窗口法,(即内层循环不回退的双重循环)。
计算特征值的的优化还可以通过预处理,先求前缀和数组,然后就可以O(1)得到任意子数组A[i...j]的和,或者做一棵区间树,之后都可以O(logn)查询。
关于子数组的条件,满足某种pattern的(回文,有序)没办法,就得O(n)的去check,关于统计特征,有些可以转换为特征值,比如:
1)01串找最长的01出现次数相等的子串,通过把‘0’当成-1,‘1’当成1,可以转化为最长的sum为0的子数组问题
2)最长的可整合子数组(排序后连续,e.g. [3, 2,1]),可以转化为,max - min == j - i
import sys
def longestConsecutiveSubArray(A):
ans = 0
for i in xrange(len(A)):
lo, hi, s = sys.maxint, -sys.maxint - 1, set()
for j in xrange(i, len(A)):
if A[j] in s: continue
s.add(A[j])
lo, hi = min(lo, A[j]), max(hi, A[j])
if hi - lo == j - i:
ans = max(ans, j - i + 1)
return ans