目录
一、题目描述
给定一个数字字符串 S,比如 S = "123456579",我们可以将它分成斐波那契式的序列 [123, 456, 579]。
形式上,斐波那契式序列是一个非负整数列表 F,且满足:
- 0 <= F[i] <= 2^31 - 1,(也就是说,每个整数都符合 32 位有符号整数类型);
- F.length >= 3;
- 对于所有的0 <= i < F.length - 2,都有 F[i] + F[i+1] = F[i+2] 成立。
另外,请注意,将字符串拆分成小块时,每个块的数字一定不要以零开头,除非这个块是数字 0 本身。
返回从 S 拆分出来的任意一组斐波那契式的序列块,如果不能拆分则返回 []。
示例 1:
输入:"123456579"
输出:[123,456,579]
示例 2:
输入: "11235813"
输出: [1,1,2,3,5,8,13]
示例 3:
输入: "112358130"
输出: []
解释: 这项任务无法完成。
示例 4:
输入:"0123"
输出:[]
解释:每个块的数字不能以零开头,因此 "01","2","3" 不是有效答案。
示例 5:
输入: "1101111"
输出: [110, 1, 111]
解释: 输出 [11,0,11,11] 也同样被接受。
提示:
- 1 <= S.length <= 200
- 字符串 S 中只含有数字。
二、解题思路
这题是经典的回溯算法的题。
个人经验是学习任何一种新的算法思想,在理解了理论知识后加以适当的练习,才能把这种思想熟练的掌握,如果练题量不足的话,后面遇到经典直接的题也许会做,但是如果稍加变化,可能就不会做了。练题的目的是为了积累经验,有了经验之后做题就是记忆反射加上一些小变化了。没错,说的就是我练题不够…
关于回溯算法,个人的一些小经验(练题并不多,并不能保证正确):
回溯算法有两种基本特征:
1、每一步可能有多个可选项,一开始时并不知道该选哪一个(选哪一个都有可能);
2、每一步的选择有连锁反应,当某一步的选择不满足要求时则需要回退一步再继续选择试探。
说回本题,本题的可选项是选择哪几个字符构建成一个数字并不确定,只有当所有的数字都构建完了,并且满足斐波那契数列的形式则算构建成功;所以只有循环遍历所有可能的字符串前缀,查看是否满足要求,如果满足要求就把构建的数组返回。另外注意dfs剪枝的情况:数字不能有前置0;整形和存在越界情况。
三、代码实现
#include <bits/stdc++.h>
using namespace std;
vector<int> res;
bool backtrack(string s, int index) {
//递归出口:字符串走完了,并且数组元素个数大于等于3
if (index == s.size() && res.size() >= 3) return true;
long long curr = 0;
//循环遍历每一个字符,寻找一个可能满足要求的数字
for (int i = index; i < s.size(); i++) {
//前置0直接不能成功划分
if (i > index && s[index] == '0') return false;
curr = curr * 10 + s[i] - '0';
//当前划分出的数字已经大于int的最大值了,后续只能会更大了,不用继续再分
if (curr > INT_MAX) return false;
//将得到的满足要求的数字放进数组中,注意int型数据越界的问题
if (res.size() < 2 || (long long)res[res.size() - 2] + (long long)res[res.size() - 1] == curr) {
res.push_back(curr);
//对后续字符继续划分,如果成功划分,返回true
if (backtrack(s, i + 1)) {
return true;
}
//划分不成功说明需要回溯
res.pop_back();
}
}
return false;
}
vector<int> splitIntoFibonacci(string S) {
backtrack(S, 0);
return res;
}
int main() {
string S = "11235813";
vector<int> ans = splitIntoFibonacci(S);
for (auto& x : ans) {
cout << x << " ";
}
return 0;
}