尋找和最大的最長子串
問題:
給你一個整型陣列,其中的元素有正有負也有0,要求你找到一個連續的子串,子串的所有元素之和是所有的連續子串中最大的,而且是非負的。(應該講清楚了吧?^_^)
解決方法:
最簡單而又最高效的方法應該就是《程式設計珠璣》上介紹的掃描法了吧。其基本思想是假設以【0~(i-1)】為下標區間的子串中的最大子向量的元素之和,以及以下標 i-1 為截止元素的最大子向量之和已經求出,則當掃描到下標為 i 的元素時,這兩個最大和應該做如何改變?
具體操作可以這樣:
通過一個變數儲存遍歷到當前位置為止所找到的最大連續子串的元素之和,假設為max
通過另一個變數temp用來儲存以當前位置上的元素作為最後一個元素的連續子串的元素之和,要求該連續子串的元素之後是非負的。
一開始,max和temp都是0,然後從陣列第一個元素開始掃描,當掃描到一個元素a[index]時,看看將這個元素加入到當前連續子串之後,更新的temp是否會小於0,如果小於0,則temp重置,當前連續子串變成空串,然後繼續往下掃描;如果temp不小於0,則當前元素加入當前連續子串,然後看temp是否比當前記錄的max要大,如果是,則用temp來更新max,以記錄當前真正的最大子串之和。如此一直到掃描一遍結束,則最大子串的和就知道了。當然,如果還需要知道和最大的最長子串的各個元素,則還要多加幾個變數,多做一些判斷。
程式碼:
int findMaxSequence(const int * seq, int n, int * start, int * end)
/* seq為傳入的整型陣列,n為陣列長度, start, end分別用來返回所找到的最長子串的起始下標和截止下標 */
{
int maxsofar = 0, maxendinghere = 0;
int starthere = 0;
*start = *end = 0;
for ( int i = 0; i < n; i++ )
{
if ( maxendinghere + seq[i] >= 0 )
maxendinghere = maxendinghere + seq[i];
else
{
maxendinghere = 0;
starthere = i+1;
}
if ( maxendinghere > maxsofar ||
((maxendinghere == maxsofar) && (*end - *start) < (i - starthere))
{
maxsofar = maxendinghere;
*start = starthere;
*end = i;
}
}
return maxsofar;
}
延伸下,對本問題可以產生至少2個變體問題(也是《程式設計珠璣》上來的),一個是尋找最長的連續子串,使得和最小且非正,另一個是使得和最接近於某個整數m。
前一個問題比較簡單,其實就是和最大這個問題的一個逆問題而已。第二個問題ms有點難,反正我現在還不知道怎麼一遍搞定^_^。
題外話:
本題是我們學校05年考研的最後一道題,也是當年上機考的最後一道,更是06年google在浙大的一道筆試題,慚愧的是,我居然3次都沒有做出這題,可見演算法能力實在是薄弱啊。google筆試之後和同學一起研究了下,也找到了個一次遍歷搞定的方法,但操作起來比較麻煩,而且不太容易懂,直到後來看了《程式設計珠璣》一書才發現原來最好的方法是多麼簡單。後來還發現,很多之前一直不會的演算法,都能在《演算法導論》或《程式設計珠璣》上找到簡單高效的答案,再一次感嘆演算法才是王道啊,大學6年看了那麼多書就是沒看多少演算法書,真是失誤大了!