Leetcode 306. Additive Number
Problem Explanation:
The problem is asking to determine if a string of digits can form an additive sequence or not. An additive sequence is a sequence in which every number (starting from the third) is the sum of its two predecessors. In our scenario, the string should contain at least three numbers to form such a sequence. The given string will contain numbers ranging from '0' to '9' and its length will be in the range [1, 35]. It's worth noting that numbers in the sequence can't have leading zeros. For instance, '1,2,03' or '1,02,3' are invalid sequences.
Let's look at an example to visualize the problem: Input: "112358" Output: true Explanation: This is an additive sequence: 1, 1, 2, 3, 5, 8. Here, 1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, and 3 + 5 = 8
Solution Approach:
We are going to use brute force and depth-first search (DFS) in our solution.
We loop through given string's half length because at least three numbers (or digits in this case) are needed to form a valid sequence and innumerable sums can be constructed with or from the first half of the numbers. We also keep track of whether the current number starts with a zero. If it does, it cannot form a valid sequence and so we return false.
Next, we get the first number (or digit), 'firstNum', according to our loop's index, 'i' .
Then, nested within this loop, we create another loop that starts from 'i+1'. This loop is to segment the second number, 'secondNum' of the sequence. The maximum length for 'secondNum' would ideally be the remaining length of the sequence but the constraints of the problem require it to be less than the remaining length of sequence for sum validity.
With each 'secondNum' calculation, we check if the currently traversed number starts with zero. If it does, we break from the loop because it can't form a valid sequence. If it doesn't, we get the 'secondNum' according to the index, 'j'.
The 'dfs' function then takes in the original string, 'firstNum', 'secondNum' and the index at which the start of the 3rd number (index 's') in the sequence for our recursive validation checks.
In the 'dfs' function, we find the sum of first two numbers and convert it to string so that it can be compared with the rest of the string digits to check if it forms a valid sequence.
If the sequence from index 's' starts with our now computed sum of two digits, 'thirdNumStr', and it continues to form a valid sequence up to the end of original sequence, our implementation returns true, meaning the sequence can form a valid additive sequence. If the sequence turns invalid at any of the recursive validation checks, false is returned.
Now, let's dive into coding the solution in different languages.
Python Solution:
1 2python 3class Solution: 4 def isAdditiveNumber(self, num: str) -> bool: 5 length = len(num) 6 for i in range(1, length // 2 + 1): 7 if i > 1 and num[0] == '0': 8 return False 9 for j in range(i+1, length): 10 first, second, rest = int(num[:i]), int(num[i:j]), num[j:] 11 if rest and (rest[0] == '0' and len(rest) > 1): 12 break 13 while rest: 14 third = str(first + second) 15 if not rest.startswith(third): 16 break 17 else: 18 first, second = second, int(third) 19 rest = rest[len(third):] 20 if not rest: 21 return True 22 return False
Java Solution:
1
2java
3class Solution {
4 public boolean isAdditiveNumber(String num) {
5 int n = num.length();
6 for(int i=1; i <= n/2; ++i)
7 for(int j=i+1; j<n; ++j)
8 if(isValid(i, j, num)) return true;
9 return false;
10 }
11
12 private boolean isValid(int i, int j, String num) {
13 if(num.charAt(0) == '0' && i > 1) return false;
14 if(num.charAt(i) == '0' && j-i > 1) return false;
15 String sum;
16 Long x1 = Long.parseLong(num.substring(0, i));
17 Long x2 = Long.parseLong(num.substring(i, j));
18 for(int start = j; start != num.length(); start += sum.length()) {
19 x2 = x2+x1;
20 x1 = x2-x1;
21 sum = x2.toString();
22 if(!num.startsWith(sum, start)) return false;
23 }
24 return true;
25 }
26}
JavaScript Solution:
1
2javascript
3var isAdditiveNumber = function(num) {
4 for(let i=1; i<=num.length/2; i++)
5 for(let j=i+1; j<num.length; j++)
6 if(isValid(i, j, num)) return true;
7 return false;
8};
9
10const isValid = (i, j, num) => {
11 if(num[0] == '0' && i > 1) return false;
12 if(num[i] == '0' && j-i > 1) return false;
13 let sum;
14 let x1 = Long.fromValue(num.substring(0, i));
15 let x2 = Long.fromValue(num.substring(i, j));
16 for(let start = j; start != num.length; start += sum.length) {
17 x2 = x2.add(x1);
18 x1 = x2.subtract(x1);
19 sum = x2.toString();
20 if(!num.startsWith(sum, start)) return false;
21 }
22 return true;
23}
C++ Solution:
1
2c++
3class Solution {
4public:
5 bool isAdditiveNumber(string num) {
6 int n = num.length();
7 for (int i = 1; i <= n / 2; ++i)
8 for (int j = i + 1; j < n; ++j)
9 if (isValid(i, j, num)) return true;
10 return false;
11 }
12
13 bool isValid(int i, int j, string num) {
14 if (num[0] == '0' && i > 1) return false;
15 if (num[i] == '0' && j-i > 1) return false;
16 string sum;
17 long long x1 = stoll(num.substr(0,i));
18 long long x2 = stoll(num.substr(i,j-i));
19 for (int start = j; start != num.size(); start += sum.length()) {
20 x2 = x2 + x1;
21 x1 = x2 - x1;
22 sum = to_string(x2);
23 if (!num.substr(start).compare(0, sum.length(), sum) == 0) {
24 return false;
25 };
26 }
27 return true;
28 }
29};
C# Solution:
1
2csharp
3public class Solution {
4 public bool IsAdditiveNumber(string num) {
5 for (int i = 1; i <= num.Length / 2; i++)
6 for (int j = i + 1; j < num.Length; j++)
7 if (IsValid(i, j, num)) return true;
8 return false;
9 }
10
11 private bool IsValid(int i, int j, string num) {
12 if (num[0] == '0' && i > 1) return false;
13 if (num[i] == '0' && j - i > 1) return false;
14 string sum;
15 ulong x1 = ulong.Parse(num.Substring(0, i));
16 ulong x2 = ulong.Parse(num.Substring(i, j - i));
17 for (int start = j; start != num.Length; start += sum.Length) {
18 x2 = x2 + x1;
19 x1 = x2 - x1;
20 sum = x2.ToString();
21 if (!string.Equals(num.Substring(start, sum.Length), sum)) return false;
22 }
23 return true;
24 }
25}
In either solution, the significant component is the depth-first search function which helps us traverse the sequence from one index to another, ensuring the sequence validity by comparing the sum of two preceding numbers with the forthcoming numbers in the sequence.
The time complexity is O(n^3), where n is the number of digits present in the string num, i.e., n = num.length().Giving a clearer explanation;
In the worst case scenario, we are performing three nested loops, hence the time complexity is O(n^3). The first loop (i-loop) and the second loop (j-loop) is picking the first two initial numbers in the sequence. These two initial loops have time complexity of O(n^2). Then in the third loop, we are using while-loop to generate the remaining sequence, the time complexity is O(n). So the total time complexity is O(n^3).
The space complexity is O(n) as we make use of a few variables that scale proportionally to the length of the string num. There is rest variable where in the worst case could be a copy of the num. Also, extra space is used for function call stack that goes as deep as n in the worst case scenario.
Got a question?ย Ask the Teaching Assistantย anything you don't understand.
Still not clear? Ask in the Forum, ย Discordย orย Submitย the part you don't understand to our editors.