給一個浮點數序列,取最大乘積連續子串的值,例如 -2.5,4,0,3,0.5,8,-1,則取出的最大乘積連續子串爲3,0.5,8。也就是說,上述數組中,3 0.5 8這3個數的乘積30.58=12是最大的,並且是連續的。java
此最大乘積連續子串與最大乘積子序列不一樣,請勿混淆,前者子串要求連續,後者子序列不要求連續。也就是說,最長公共子串(Longest CommonSubstring)和最長公共子序列(LongestCommon Subsequence,LCS)是:面試
更簡略地說,前者(子串)的字符的位置必須連續,後者(子序列LCS)則沒必要。好比字符串「 acdfg 」同「 akdfc 」的最長公共子串爲「 df 」,而它們的最長公共子序列LCS是「 adf 」,LCS可使用動態規劃法解決。算法
或許,讀者初看此題,可能立馬會想到用最簡單粗暴的方式:兩個for循環直接輪詢。編程
但這種蠻力的方法的時間複雜度爲O(n^2),可否想辦法下降時間複雜度呢?數組
考慮到乘積子序列中有正有負也還可能有0,咱們能夠把問題簡化成這樣:數組中找一個子序列,使得它的乘積最大;同時找一個子序列,使得它的乘積最小(負數的狀況)。由於雖然咱們只要一個最大積,但因爲負數的存在,咱們同時找這兩個乘積作起來反而方便。也就是說,不但記錄最大乘積,也要記錄最小乘積。markdown
假設數組爲a[],直接利用動態規劃來求解,考慮到可能存在負數的狀況,咱們用maxend來表示以a[i]結尾的最大連續子串的乘積值,用minend表示以a[i]結尾的最小的子串的乘積值,那麼狀態轉移方程爲:spa
maxend = max(max(maxend * a[i], minend * a[i]), a[i]); minend = min(min(maxend * a[i], minend * a[i]), a[i]);
初始狀態爲maxend = minend = a[0]。code
參考代碼以下:blog
public static double maxProductSubstring(double[] a) { double maxEnd = a[0]; double minEnd = a[0]; double maxResult = a[0]; for (int i = 1; i < a.length; ++i) { double end1 = maxEnd * a[i], end2 = minEnd * a[i]; maxEnd = Math.max(Math.max(end1, end2), a[i]); minEnd = Math.min(Math.min(end1, end2), a[i]); maxResult = Math.max(maxResult, maxEnd); } return maxResult; }
動態規劃求解的方法一個for循環搞定,因此時間複雜度爲O(n)。字符串