給定一個僅包含數字的字符串,判斷能否拆分成至少三個數字構成的「累加序列」:除前兩個數外,每個數等於前兩數之和。數字不允許前導零(單個 0 除外)。
例子:"112358" → 1,1,2,3,5,8 ✅;"199100199" → 1,99,100,199 ✅。
序列完全由前兩個數決定。窮舉所有合法的前兩數切分點,然後遞歸驗證剩餘字符串是否匹配後續和值。
'0' 直接非法。n-2,第二個數最長 n-i-1,且若首字符為 '0' 只能取長度 1。
std::string addStrings(const std::string& a, const std::string& b) {
std::string res;
int i = a.size() - 1, j = b.size() - 1, carry = 0;
while (i >= 0 || j >= 0 || carry) {
int sum = carry;
if (i >= 0) sum += a[i--] - '0';
if (j >= 0) sum += b[j--] - '0';
res.push_back('0' + (sum % 10));
carry = sum / 10;
}
std::reverse(res.begin(), res.end());
return res;
}
bool dfs(const std::string& remaining, const std::string& prev1, const std::string& prev2) {
if (remaining.empty()) return true; // 全部匹配成功
if (remaining[0] == '0') return false; // 剩餘以 0 開頭但非單個 0 → 非法
std::string sum = addStrings(prev1, prev2);
if (remaining.size() < sum.size()) return false; // 剩餘長度不足
if (remaining.substr(0, sum.size()) != sum) return false; // 前綴不匹配
// 匹配成功,遞歸驗證剩餘部分
return dfs(remaining.substr(sum.size()), prev2, sum);
}
bool isAdditiveNumber(std::string num) {
int n = num.size();
if (n < 3) return false;
for (int i = 1; i <= n - 2; ++i) { // 第一個數長度
if (i > 1 && num[0] == '0') break; // 前導零剪枝
std::string first = num.substr(0, i);
for (int j = 1; j <= n - i - 1; ++j) { // 第二個數長度
if (j > 1 && num[i] == '0') break; // 前導零剪枝
std::string second = num.substr(i, j);
if (dfs(num.substr(i + j), first, second))
return true;
}
}
return false;
}
O(n^3)。雙重循環 O(n^2),每次 DFS 最深 O(n) 步,每步大數加法 O(n)。實際上剪枝極強,遠快於理論上界。O(n) 遞歸棧 + 字符串臨時空間。| 輸入 | 輸出 | 說明 |
|---|---|---|
"112358" | true | 經典斐波那契 |
"199100199" | true | 大數跨度 |
"1023" | false | 1,0,2,3 但 0 後接 2 非法(前導零) |
"000" | true | 0,0,0 單個 0 合法 |
"1203" | false | 1,2,03 前導零 |
本解法通過字符串加法天然解決溢出問題,無需額外大數庫。若語言內置大數(Python/Java BigInteger),可直接用整數運算簡化代碼。
「枚舉首兩數 + DFS 驗證」是解決累加數問題的標準範式。關鍵在於:
此題是「字符串處理 + 遞歸 + 大數運算」的優秀綜合訓練。