給一個正整數陣列 arr ,跟一整數 target ,最找出二子陣列各自總和皆為target且二子陣列不重壘的最小長度總和。若沒有回傳 -1。
關於找子陣列(注意要為連續的或是稱序列)總和為target,通常用計算其累進值,accu[0] = arr[0], accu[1] = accu[0] + arr[0], accu[-1] = sum(arr)。
如果 accu[i] - target 在 accu 內,就代表有對應另外一個的連續子陣列存在。透過 accu[j] - accu[i] 可以找到 arr 任位置的連續子陣列總和。
在計算累進值的同時,就可以開始找目標值,也是這種算法的優勢,如果搭配 hash類 的查詢,只要累進值算完,就能找完所有可能,相當於 O(N) Time。
首先要決定 ans 的預設值,因為如果拿 -1 當預設去比小,最後會是 -1 ,需要找一個比 -1 大且不會有可能出現的值,最大可能為 len(arr),所以 ans 預設為 len(arr) + 1。
一旦有 accu[i] - target 出現,表示現在到找一段總和為 target,這一段的頭位置為 accu[i] - target 在 accu 中的位置,尾位置為 i 。
如果前面已經有一段,只要前一段的尾位置 >= 這一段的頭位置,代表兩者沒有重壘到,就能拿二段長度相交去更新 ans 。
每個找到的子陣列其尾位置(i)是最先被知道的,之後的找到的子陣列只要其頭位置在此或之後,都可以與之形成合格的可能總長度。
如在 i 位置上找到總長度最短的 shortest,在 i 位置之後的找到每段其頭位置在 i 或 i 之後,都能與 shortest 組合起來。
因此可這麼看,在i與之後的位置最小長都是 shortest。
每個位置都有一個 shortest,整體用 shortests 紀錄。
shortests [i] 為頭位置在 i 時能組合最小的長度。
每位置不論有沒有找到,都要把 shortest 保存在 shortests [i]。
(※shortest: [default,s1,s1,s1,s2,s2,s2,s2,s3,...,shortest] )
找到一段,就用該段的頭位置 headPos 去對 shortest[headPos] 得到前方可組合的最小長度,二者長度相加去更新ans。
shortest 預設也為 len(arr)+1 ,如果前方沒有找到或沒能組合的,也不會改動 ans。
class Solution: def minSumOfLengths(self, arr: List[int], target: int) -> int: accuPos = {0:-1} accu = 0 ans = shortest = len(arr)+1 shortests = [len(arr)+1] * len(arr) for i, num in enumerate(arr): accu += num accuPos[accu] = i if accu - target in accuPos: length = i - accuPos[accu - target] if accuPos[accu - target] > -1: ans = min(ans, length + shortests[accuPos[accu - target]]) shortest = min(shortest, length) shortests[i] = shortest return -1 if ans == len(arr)+1 else ans