这是一道leetcode上的题。我来简单翻译一下。leetcode上相应的代码。代码整体思路就是遍历整个数组,然后求出每个数字他在子数组中最小的次数。
有种方法就是先求出每个数在他前面比他小的数的位置,以及在他后面比他小的位置。
[3,1,2,4,1,0]以这个数组为例,我们首先求出3为最小数的情况数。我们先在这个数组的最前面加上一个数,这个数得是小于其他所有数的,我们把这个数放在最前面。然后我们一共给出,是五个参数,便于计算。我们来一步步分析代码含义以及作用。
def gaiji(arr):#就是函数名
arr.append(-1)#append是在最前面加上去的,原数组最小数比-1小
ans, st = 0, [-1] # 哨兵
for r, x in enumerate(arr):
#enumerate这个函数是分别输出索引以及数组中对应数的大小。
while len(st) > 1 and arr[st[-1]] >= x:
#st的长度不能小于1,不然不能pop()之后没有st[-1]这个数字了。后面这个条件是将原数组分为一个一个单调递增的小区间。然后从小区间最前面的一个数开始算起。我们就将例子分成[3][1,2,4] [1]。
i = st.pop()#也是最前面的一个数
ans += arr[i] * (i - st[-1]) * (r - i) # 累加贡献
st.append(r)
#开始对举例分析。第一次for循环时,st的长度时1,不进行while里的循环内容,然后,st加上第一个索引,0。st=[-1,0]。第二次for中,原数组第2个数是1,st[-1]是上一个数的索引,我们就找到在3前面第一个比他小的数字了。就是1。然后进入while循环,我们要知道,while循环一次就是将原数组中一个的贡献值给求出来了。pop出来的数正是我们上句话说到的那个数的索引。用i-st[-1]的意思是左边比他小的数与他的距离。这里初始化st的效果就出现了。(i - st[-1]) * (r - i)这个式子正是这个数出现的次数,第一小块就求出来了。
开始分析第二轮,st在pop出一个数后,加上了1的索引,st中-1的位置始终保持不变。然后一直没有执行while循环直到,4>1停止,因为此时4这个数已经找到前面比他小的数了。很容易能得到,r-i是1,此时st=[-1,1,2],后面比他小的位置正是索引相差1,因为这个是单调递增的函数。这次while循环就是只加了4*1。再回到while循环,我们发现2还是比这个1大,所以,while继续,(如果不满足后面这个条件,后面再说。)很明显,i-st[-1]还是1,但是r-i是变成了2.所以这次while让ans多加了2*1*2。再看1,有意思的出现了,他是等于此时的x的,但是根据while条件,他也会执行while循环。此时st只有一个数了,也即是那个不变的-1,所以,i-st[-1]是2。此次循环让ans多加了1*2*3=6。这个小块也就跟着完成了。
最后剩下来一个1,为何要让我们最开始添加的那个数小于所有数组里的数的原因就是避免向前找时找多了。此时的1直接进入while,让ans加上1*5*1.
以上便是例子的分析。
现在说说,上面提到的那个问题。如果[1,2,4]是[0,2,4]呢。这个时候,2,4的分析不变。我们不会运行while循环,而是直接让st加上1的索引,st=[-1,1,4],又到最后一个数了,和上面分析的一样,我们开始进入while循环,这个1分析不变,然后才是那个0。这个0出现的次数是,2*4.所以,此代码依然没有问题。
return ans % (10 ** 9 + 7)#这个就很简单了。取余,因为题目给定了范围。
完整代码:本人电脑运行无误。
def gaiji(arr):
arr.append(-1)
ans, st = 0, [-1]
for r, x in enumerate(arr):
while len(st) > 1 and arr[st[-1]] >= x:
i = st.pop()
ans += arr[i] * (i - st[-1]) * (r - i) # 累加贡献
st.append(r)
return ans % (10 ** 9 + 7)