[LeetCode] Arithmetic Slices II - Subsequence 算數切片之二 - 子序列


 

A sequence of numbers is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.

For example, these are arithmetic sequences:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9

The following sequence is not arithmetic.

1, 1, 2, 5, 7

 

A zero-indexed array A consisting of N numbers is given. A subsequence slice of that array is any sequence of integers (P0, P1, ..., Pk) such that 0 ≤ P0 < P1 < ... < Pk < N.

A subsequence slice (P0, P1, ..., Pk) of array A is called arithmetic if the sequence A[P0], A[P1], ..., A[Pk-1], A[Pk] is arithmetic. In particular, this means that k ≥ 2.

The function should return the number of arithmetic subsequence slices in the array A.

The input contains N integers. Every integer is in the range of -231 and 231-1 and 0 ≤ N ≤ 1000. The output is guaranteed to be less than 231-1.

 

Example:

Input: [2, 4, 6, 8, 10]

Output: 7

Explanation:
All arithmetic subsequence slices are:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]


這道題是之前那道Arithmetic Slices的延伸,那道題比較簡單是因為要求等差數列是連續的,而這道題讓我們求是等差數列的子序列,可以跳過某些數字,不一定非得連續,那么難度就加大了,但還是需要用動態規划Dynamic Progrmming來做。知道用DP來做是一回事,真正能做出來又是另一回事。刷題的最終目的不是背題,而是訓練思維方式,如何從完全沒思路,變為好像有點思路,慢慢推理演變找出正確解,就像順着藏寶圖的絲絲線索最終發現了寶藏一樣,是無比的令人激動和富有成就感的過程。死記硬背的話,只要題目稍稍變形一下就完蛋。這就是為啥博主喜歡看fun4LeetCode大神的帖子,雖然看范佛力扣大神的帖子像讀論文,但是有推理過程,看完讓人神清氣爽,茶飯不思,燃鵝遇到同類型的還是不會,還是要再看。好,博主皮一下就行了,就不皮幾萬了,下面來順着大神的帖子來講吧。

好,既然決定要用DP了,那么首先就要確定dp數組的定義了,剛開始我們可能會考慮使用個一維的dp數組,然后dp[i]定義為范圍為[0, i]的子數組中等差數列的個數。定義的很簡單,OK,但是基於這種定義的狀態轉移方程卻十分的難想。我們想對於(0, i)之間的任意位置j,如何讓 dp[i] 和 dp[j] 產生關聯呢?是不是只有 A[i] 和 A[j] 的差值diff,跟A[j]之前等差數列的差值相同,才會有關聯,所以差值diff是一個很重要的隱藏信息Hidden Information,我們必須要在dp的定義中考慮進去。所以一維dp數組是罩不住的,必須升維,但是用二維dp數組的話,差值diff那一維的范圍又是個問題,數字的范圍是整型數,所以差值的范圍也很大,為了節省空間,我們建立一個一維數組dp,數組里的元素不是數字,而是放一個HashMap,建立等差數列的差值和當前位置之前差值相同的數字個數之間的映射。我們遍歷數組中的所有數字,對於當前遍歷到的數字,又從開頭遍歷到當前數字,計算兩個數字之差diff,如果越界了不做任何處理,如果沒越界,我們讓dp[i]中diff的差值映射自增1,因為此時A[i]前面有相差為diff的A[j],所以映射值要加1。然后我們看dp[j]中是否有diff的映射,如果有的話,說明此時相差為diff的數字至少有三個了,已經能構成題目要求的等差數列了,將dp[j][diff]加入結果res中,然后再更新dp[i][diff],這樣等遍歷完數組,res即為所求。

我們用題目中給的例子數組 [2,4,6,8,10] 來看,因為2之前沒有數字了,所以我們從4開始,遍歷前面的數字,是2,二者差值為2,那么在dp[1]的HashMap就可以建立 2->1 的映射,表示4之前有1個差值為2的數字,即數字2。那么現在i=2指向6了,遍歷前面的數字,第一個數是2,二者相差4,那么在dp[2]的HashMap就可以建立 4->1 的映射,第二個數是4,二者相差2,那么先在dp[2]的HashMap建立 2->1 的映射,由於dp[1]的HashMap中也有差值為2的映射,2->1,那么說明此時至少有三個數字差值相同,即這里的 [2 4 6],我們將dp[1]中的映射值加入結果res中,然后當前dp[2]中的映射值加上dp[1]中的映射值。這應該不難理解,比如當i=3指向數字8時,j=2指向數字6,那么二者差值為2,此時先在dp[3]建立 2->1 的映射,由於dp[2]中有 2->2 的映射,那么加上數字8其實新增了兩個等差數列 [2,4,6,8] 和 [4,6,8],所以結果res加上的值就是 dp[j][diff],即2,並且 dp[i][diff] 也需要加上這個值,才能使得 dp[3] 中的映射變為 2->3 ,后面數字10的處理情況也相同,這里就不多贅述了,最終的各個位置的映射關系如下所示:

 

2     4     6     8     10    
     2->1  4->1  6->1  8->1
           2->2  4->1  6->1 
                 2->3  4->2
                       2->4

 

最終累計出來的結果是跟上面紅色的數字相關,分別對應着如下的等差數列:

2->2:[2,4,6]

2->3:[2,4,6,8]    [4,6,8]

4->2:[2,6,10]

2->4:[2,4,6,8,10]    [4,6,8,10]    [6,8,10]

 

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int res = 0, n = A.size();
        vector<unordered_map<int, int>> dp(n);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                long delta = (long)A[i] - A[j];
                if (delta > INT_MAX || delta < INT_MIN) continue;
                int diff = (int)delta;
                ++dp[i][diff];
                if (dp[j].count(diff)) {
                    res += dp[j][diff];
                    dp[i][diff] += dp[j][diff];
                }
            }
        }
        return res;
    }
};

 

類似題目:

Arithmetic Slices

 

參考資料:

https://leetcode.com/problems/arithmetic-slices-ii-subsequence/solution/

https://leetcode.com/problems/arithmetic-slices-ii-subsequence/discuss/92837/C++_DP_Accepted

https://leetcode.com/problems/arithmetic-slices-ii-subsequence/discuss/92822/Detailed-explanation-for-Java-O(n2)-solution

 

LeetCode All in One 題目講解匯總(持續更新中...) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM