Given a string S
of digits, such as S = "123456579"
, we can split it into a Fibonacci-like sequence [123, 456, 579].
html
Formally, a Fibonacci-like sequence is a list F
of non-negative integers such that:git
0 <= F[i] <= 2^31 - 1
, (that is, each integer fits a 32-bit signed integer type);F.length >= 3
; 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:post
Input: "123456579" Output: [123,456,579]
Example 2:url
Input: "11235813" Output: [1,1,2,3,5,8,13]
Example 3:spa
Input: "112358130" Output: [] Explanation: The task is impossible.
Example 4:code
Input: "0123" Output: [] Explanation: Leading zeroes are not allowed, so "01", "2", "3" is not valid.
Example 5:orm
Input: "1101111" Output: [110, 1, 111] Explanation: The output [11, 0, 11, 11] would also be accepted.
Note:htm
1 <= S.length <= 200
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便可,其他部分和上面的解法並無啥區別,代碼參見評論區一樓。
相似題目:
參考資料:
https://leetcode.com/problems/split-array-into-fibonacci-sequence/