[LeetCode] Split Array into Fibonacci Sequence 分割數組成斐波那契序列


 

Given a string S of digits, such as S = "123456579", we can split it into a Fibonacci-like sequence [123, 456, 579].

Formally, a Fibonacci-like sequence is a list F of non-negative integers such that:

  • 0 <= F[i] <= 2^31 - 1, (that is, each integer fits a 32-bit signed integer type);
  • F.length >= 3;
  • and F[i] + F[i+1] = F[i+2] for all 0 <= i < F.length - 2.

Also, note that when splitting the string into pieces, each piece must not have extra leading zeroes, except if the piece is the number 0 itself.

Return any Fibonacci-like sequence split from S, or return [] if it cannot be done.

Example 1:

Input: "123456579"
Output: [123,456,579]

Example 2:

Input: "11235813"
Output: [1,1,2,3,5,8,13]

Example 3:

Input: "112358130"
Output: []
Explanation: The task is impossible.

Example 4:

Input: "0123"
Output: []
Explanation: Leading zeroes are not allowed, so "01", "2", "3" is not valid.

Example 5:

Input: "1101111"
Output: [110, 1, 111]
Explanation: The output [11, 0, 11, 11] would also be accepted.

Note:

  1. 1 <= S.length <= 200
  2. S contains only digits.

 

這道題給了我們一個字符串,讓我們分割成斐波那契序列,至少要分成三個數,並且滿足斐波那契數列的性質。關於其性質,博主有個口訣可以快速記憶,那就是大學食堂里今天的湯是昨天的湯加上前天的湯。題目中給的例子挺多的,便於理解題意,其中例子4還強調了不能有leading zeros。但是關於overflow的test case卻只字未提,害的博主fail了N多次,才最終handle了所有的溢出的錯誤。由例子5我們可以看出,符合題意的數列其實可能不止一種,但是本題就讓返回一個就行了。不管返回幾個,總之不是求極值,DP在這里就不好使了,只能用遞歸了,由於不知道如何分割,所以肯定需要遍歷所有的情況。我們用一個數組out來記錄已經組成的序列,用結果res來保存結果。當out數組的個數大於等於3,並且已經遍歷完了字符串S,那么此時就是可以把out數組中的內存賦值給結果res了,那么之后只要檢測結果res不為空時,直接返回就可以了,這是個很好的剪枝操作,因為此題只需要一個正確答案即可(返回所有情況將作為follow up在本文的底部討論)。

現在來考慮遞歸函數的主體該怎么寫,既然不知道要如何分割,那么就要嘗試所有的情況,一個數字,兩個數字,一直到末尾,那么就可以遍歷字符串S,然后取子串即可。但從什么位置開始呢,每次都從頭嗎,這道題都數字不能重復使用,所以應該用個變量start來記錄當前遍歷到的位置,那么我們從start位置起,每次取 i-start+1 長度的子串 cur,此時在轉為int之前,需要先處理leading zeros的情況,判斷若cur長度大於1,且首字符為0,直接break,還就是若cur的長度大於10,也break,為啥呢?因為整型的最大值是 2147483647,只有10位,所以當cur長度大於10時,一定會溢出。當cur長度為10時,也有可能溢出,這個在之后處理。好,現在將cur轉為長整型 long,因為長度為10也可能溢出,所以要先轉為長整型,然后在判斷若大於整型最大值 INT_MAX,直接break。接下來就要考慮是否要加入out數組了,當out數字的個數不到2個的時候,我們可以直接加入當前數字,若大於等於2個,需要考慮是否滿足斐波納切數列的性質,即當前數字是否等於前兩個數字之和,滿足的話才加入,不然就跳過,注意這里不能直接break,因為之后的數字也許可能滿足要求。加入out數組之后,就可以調用遞歸了,此時起始位置傳入 i+1,之后再恢復out的狀態即可,參見代碼如下:

  

class Solution {
public:
    vector<int> splitIntoFibonacci(string S) {
        vector<int> res, out;
        helper(S, 0, out, res);
        return res;
    }
    void helper(string& S, int start, vector<int>& out, vector<int>& res) {
        if (!res.empty()) return;
        if (start >= S.size() && out.size() >= 3) {
            res = out; return;
        }
        for (int i = start; i < S.size(); ++i) {
            string cur = S.substr(start, i - start + 1);
            if ((cur.size() > 1 && cur[0] == '0') || cur.size() > 10) break;
            long num = stol(cur), len = out.size();
            if (num > INT_MAX) break;
            if (out.size() >= 2 && num != (long)out[len - 1] + out[len - 2]) continue;
            out.push_back(num);
            helper(S, i + 1, out, res);
            out.pop_back();
        }
    }
};

 

討論:這道題只讓我們返回了一個斐波那契數列,一個很好的follow up就是返回所有滿足題意的序列,就像例子5一樣,把兩種符合題意的組合都返回出來。其實改起來相當的容易,只需要將結果res換成一個二維數組來保存所有的情況,然后在遞歸函數中,首先判斷如果已經遍歷到了S的末尾,並且out數組中的個數大於等於3了,那么將out數組加入結果res即可,其余部分和上面的解法並沒有啥區別,代碼參見評論區一樓。

 

類似題目:

Additive Number

Fibonacci Number

 

參考資料:

https://leetcode.com/problems/split-array-into-fibonacci-sequence/

https://leetcode.com/problems/split-array-into-fibonacci-sequence/discuss/133936/short-and-fast-backtracking-solution

 

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


免責聲明!

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



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