Split Array into Fibonacci Sequence

Memorial Day Problem: https://leetcode.com/problems/split-array-into-fibonacci-sequence/description/

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.
One strategy to solve the problem is to do an N^2 nested-loop thru the array (given that the array length is 200, this would cost you 40,000, which is reasonable) where the outer-loop corresponds to the F1 in the Fibonacci sequence and the  inner-loop corresponds to the F2 in the Fibonacci sequence. From that point on you'll calculate F3 (simple: F3 = F1 + F2) and will check if the rest of the string S forms a valid Fibonacci sequence. Given that the Fibonacci sequence grows up exponentially, it would mean that this check would run in LogN, for a total of N^2LogN complexity, roughly less than 400,000 iterations which is very small and fast. Code is below, Happy Memorial Day!!! Marcelo.


public class Solution
{
public IList<int> SplitIntoFibonacci(string S)
{
List<int> retVal = new List<int>();

int f1 = 0;
for (int i = 0; i < S.Length; i++)
{
f1 = 10 * f1 + (S[i] - '0');
if (f1.ToString().Length != i + 1) continue;

int f2 = 0;
for (int j = i + 1; j < S.Length - 1; j++)
{
f2 = 10 * f2 + (S[j] - '0');
if (f2.ToString().Length != j - i) continue;

int f3 = f1 + f2;
if (Valid(f2, f3, S.Substring(j + 1)))
{
retVal.Add(f1);
retVal.Add(f2);
S = S.Substring(f1.ToString().Length + f2.ToString().Length);
while (S.Length != 0)
{
retVal.Add(f3);
S = S.Substring(f3.ToString().Length);
f1 = f3;
f3 = f3 + f2;
f2 = f1;
}

return retVal;
}
}
}

return retVal;
}

public bool Valid(int previous, int current, string S)
{
if (S.Length == 0) return true;
if (!S.StartsWith(current.ToString())) return false;
return Valid(current, previous + current, S.Substring(current.ToString().Length));
}
}

Comments

  1. Awesome problem and a very nice, Marcelo! Your solution takes advantage of the problem statement very well! My solution is not nearly as fancy and uses a good old backtracking with tree pruning. In practice it works pretty fast (4ms) though :)

    class Solution {
    private:
    bool canSplit(const string& s, int idx, vector& state) const {
    if (idx == s.size() && state.size() >= 3) return true; // F.length >= 3
    long num = 0;
    for (int i = idx; i < s.size(); ++i) {
    if (i != idx && s[idx] == '0') return false; // each piece must not have extra leading zeroes
    num = num * 10 + (s[i] - '0');
    if (num > numeric_limits::max()) return false; // 0 <= F[i] <= 2^31 - 1
    if (state.size() >= 2 && (state[state.size()-2] + state.back() != num)) continue; // F[i] + F[i+1] = F[i+2]
    state.push_back(num);
    if (canSplit(s, i+1, state)) return true;
    state.pop_back();
    }
    return false;
    }
    public:
    vector splitIntoFibonacci(const string& S) {
    vector state;
    if (canSplit(S, 0, state)) return state;
    return {};
    }
    };

    https://leetcode.com/problems/split-array-into-fibonacci-sequence/discuss/135687/4ms-backtracking-in-C++

    Cheers,
    Taras

    ReplyDelete

Post a Comment

Popular posts from this blog

Advent of Code - Day 6, 2024: BFS and FSM

Golang vs. C#: performance characteristics (simple case study)

Advent of Code - Day 7, 2024: Backtracking and Eval