經典問題 最大子串和

2022-03-11 17:56:29 字數 3475 閱讀 2439

最近幾天好好的研究了一下這個問題。

問題本身就不多說了,求一串數字中的所有子串中,和最大的乙個子串。例如:

輸入:-10 5 2 3 4 -5 -23 3 7 -21

輸出:14 5 4

一、各種方法

方法1:

maxsofar = 0

for i = [0

,n)

for j =[i,n)

sum = 0

for k =[i,j]

sum +=x[k]

maxsofar = max(maxsofar,sum)

這是最直接最暴力的方法,我沒有寫,時間複雜度為o(n3),明顯這裡面有很多重複的運算,我們可以很容易的把時間複雜度降為o(n2)。實際上,我第一印象就是下面這個

方法2:

int

s,e;

int maxsum = -999999

;int tempsum = 0

;for(int i=0;i)

}}

這個方法理解為,以x[i]開頭的所有子串中,和最大的乙個。比列舉所有的 i 和 j 減少了計算量。下面乙個比較重要的方法,雖然在時間複雜度上面沒有提高,卻包含了乙個應對區間問題很重要的技巧。

方法3:

cumarr[-1] = 0

for i = [0

,n) cumarr[i] = cumarr[i-1] +x[i]

maxsofar = 0

for i = [0

,n)

for j =[i,n)

sum = cumarr[j] - cumarr[i-1

] maxsofar = max(maxsofar,sum)

這個演算法中,比較重要的一點事注意到子串和x[i...j] = cumarr[j] = cumarr[i-1],這個經驗可以用在區間問題上。乙個簡單的列子:

n個收費站之間有n-1段路,每段路花費為p,用o(1)時間求任意兩個收費站的之間的花費,要求空間為o(n)。

萬能的二分能不能用到這個問題上,顯然是可以的。在這個二分的過程中,需要注意的就是,合併結果的時候,需要注意到,跨左邊跟右邊的子串和的計算,最後就是在左邊的最大子串、右邊的最大子串、中間跨界的最大串這三者中取最大值。

方法4:

1

int max_sub(int m,intn)2

21}22int

rmax,b;

23 rmax = sum = b = 0;24

for(int i=k+1;i<=n;i++)

2532}33

int max_l =max_sub(m,k);

34int max_r = max_sub(k+1

,n);

35int result = max(lmax+rmax,max(max_l,max_r));

36/*

37how to record the start and the end

38*/

39return

result;

40 }

對於這個二分,還有乙個待解決的問題,我想嘗試一下,記錄最大子串的起始位置和結束位置。個人對這種遞迴的理解確實不夠,還沒能夠實現記錄起始和結束位置。看來我還是得抽空好好把遞迴這個玩意兒好好理解一下。二分的話,顯然時間複雜度為o(n*logn)。

接下來,就是o(n)的方法了,再來回顧一下方法3中,sum = cumarr[j] - cumarr[i-1]。要使得sum的值最大,顯然cumarr[j]越大,cumarr[i-1]的值越小,sum的值越大。於是我們可以遍歷cumarr陣列,維持乙個最小的cumarr[min_s],然後取cumarr[j]-cumarr[min_s]的最大值。

方法5:

1 min_s = -1

//這裡要初始化為-1

2 cumarr[-1] = 0;3

for(int i=0;i)

4 cumarr[i] = cumarr[i-1] +input[i];

5for(int i=0;i)614

if(cumarr[min_s] >cumarr[i])

15 min_s =i;

16 }

這裡還有乙個方法6:

1

int start = 0;2

for(int i=0;i)38

else

if(maxendinghere + input[i] <= 0)9

13if(maxendinghere >maxsofar)

1419 }

-10   1  2  3  4  -5   -23   3  7    -21   (num)

-10 | 1  3  6 10  8 | -23 |  3 10 | -21   (sum)(|號為該處sum被清零)

由於10是sum的最大值,所以,紅色的那塊就是要求的範圍了。

這樣就比較好理解第六種方法。

二、思考問題:

1.證明最大子串和的時間複雜度下屆是o(n)

各位如果有思路或者資料麻煩告訴我一聲。。。無處下手啊

2.求子串和最接近0的子串。

嗯,對於這個問題,前面的經驗有 sum = cumarr[j]-cumarr[i-1],這樣的話問題就轉換成,求cumarr陣列裡面差值最小或者相等的兩個元素。

用排序的方法,再遍歷一次陣列就可以得出結果,時間複雜度為o(n*logn)。 有沒有更好的方法?

3.求子串和最接近t的子串。

這個問題,如果繼續採用問題二的方案,問題轉換成,在乙個排好序的陣列裡面找兩個值的差值為t,顯然不能達到同樣的效果。暫時沒有想到其他更好的方法。

4.m和n為整數,給定x[n],求整數i,使得 x[i] + ... + x[i+m] 的和最接近為0。(即在第二個問題的基礎上,加了條件: m個元素)

這個問題因為相差為m,掃一遍cumarr陣列貌似就ok了。

min =max_num

cumarr[-1] = 0

for i = [0

,n)

if(i+mtemp = cumarr[i+m]-cumarr[i-1

]

if (min >temp)

min =temp

start =i

end = i+m

想要透徹的理解乙個演算法真的很難,我感覺我還是不太會思考。上面大部分內容是參考程式設計珠璣上面的內容,看懂正文好像不是太難,但是後面的習題就各種傻了。也許是我看得太少的緣故吧。

接下來想要看得主題 column 4 writing correct programs

最大子串和問題

問題 給定一組數字,求連續的字串的最大的和。這裡要注意題目中是子串和而不是子串行和。子串行只要求各元素的順序與其在陣列中一致,而沒有連續的要求。如果求子序列,可直接把這組數字中的正數相加即可。最開始想著只要把這組數字中各個正數子串行分別求和,比較哪個大就行了,後來發現不對。比如 正數子串行分別為,其...

最大子串和問題(Maximum Subarray)

又乙個經典問題,對於乙個包含負值的數字串array 1.n 要找到他的乙個子串array i.j 0 i j n 使得在array的所有子串中,array i.j 的和最大。這裡我們需要注意子串和子串行之間的區別。子串是指數組中連續的若干個元素。子串行只要求各元素的順序與其在陣列中一致,而沒有連續的...

最大子串問題

例如 2,4,7,20,1,1,1,1,10,1,1,1,10,10,10,10,25,10,10,10,10,10,10,300 首先,看到這個問題我們最先想到的一種方法就是 找到所有的子串,然後通過依次比較,找到最大的。接下來我們演示一下這種方法的 public int getmaxvalue ...