1. 问题描述:
累加数是一个字符串,组成它的数字可以形成累加序列。
一个有效的累加序列必须至少包含 3 个数。除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和。
给定一个只包含数字 '0'-'9' 的字符串,编写一个算法来判断给定输入是否是累加数。
说明: 累加序列里的数不会以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。
示例 1:
输入: "112358"
输出: true
解释: 累加序列为: 1, 1, 2, 3, 5, 8 。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8
示例 2:
输入: "199100199"
输出: true
解释: 累加序列为: 1, 99, 100, 199。1 + 99 = 100, 99 + 100 = 199
进阶:
你如何处理一个溢出的过大的整数输入?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/additive-number
2. 思路分析:
① 由题目可以知道,我们是不知道输入的字符串中哪个组合最终可以满足题目条件的,所以我们先需要找到第一个满足前面两数之和等于第三个数字的例子,然后调用一个递归方法来检查剩余的在字符串中的数字是否满足条件,假如递归下去返回true那么说明当前这个字符串是满足题目要求的,假如不满足尝试在for循环中找到其余的例子然后再递归下去,所以发现一个满足条件那么直接返回true,假如所有的的例子都不满足条件那么返回false
② 对于字符串中的数字进行相加,其中相加的时候采用的是模拟数字的加法得到加法结果,其实可以不用模拟这样的加法来操作的,直接将对应范围的字符串转为数字即可,这样的话比模拟数字相加的效率更高一点,但是字符串的加法有一个优点是不用担心数字相加的时候溢出的问题,实际上在相加的时候都是以字符串的形式来做加法的
③ 另外一个难点是在递归的时候检查往下三个数字是否满足条件的时候涉及到很多位置的变换,这些截取字符串的位置就非常容易出现错误,容易出现越界,调试起来也麻烦,可以提交到领扣上再对错误的数据结合idea的debug进行调试找出遗漏的点在哪里,然后增加遗漏的代码,我们在考虑细节的时候其实简单的测试用例也是非常重要的,因为我们可通过简单的例子来确定某些参数的范围与边界
④ 由于使用的是字符串的加法,所以可能会出现前导0的情况,比如199001200假如模拟字符串加法需要对这种情况进行处理一下否则得到的结果是错误的,感觉在做字符串截取的时候会遇到很多容易越界的地方只有不断的调试才能够熟练与解决这些问题(真的感觉字符串截取的太麻烦了)
3. 代码如下:
class Solution {
public boolean isAdditiveNumber(String num) {
/*首先是需要找出第一个符合条件的位置然后再决定往下进行递归下去*/
for (int i = 0; i < num.length() / 2; ++i){
for (int j = i + 1; j < num.length(); ++j){
String cur = add(num, 0,i + 1, i + 1, j + 1);
if (j + 1 + cur.length() <= num.length()) {
String third = num.substring(j + 1, j + 1 + cur.length());
if (cur.equals(third)) {
if (j + 1 + cur.length() == num.length()) return true;
boolean res = recursion(i + 1, j + 1, j + 1, j + 1 + cur.length(), num);
if (res) return true;
}
}
}
}
return false;
}
private boolean recursion(int istart, int iend, int jstart, int jend, String num) {
//往下肯定不满足条件的
if (istart >= num.length() - 2) return false;
String cur = add(num, istart, iend, jstart, jend);
/*之前是使用一个变量来存储导致越界了因为在截取之前没有判断*/
if (jend + cur.length() > num.length() || !num.substring(jend, jend + cur.length()).equals(cur)) return false;
if (jend + cur.length() == num.length()) return true;
return recursion(iend, jend, jend, jend + cur.length(), num);
}
/*两个模拟字符串的整数相加*/
private String add(String num, int istart, int iend, int jstart, int jend) {
String istr = num.substring(istart, iend);
String jstr = num.substring(jstart, jend);
if (num.charAt(jstart) == '0' && jstr.length() > 1) return "-1";
int isadd = 0;
int ilen = istr.length() - 1;
int jlen = jstr.length() - 1;
String res = "";
int cur = 0;
while (ilen >= 0 && jlen >= 0){
cur = isadd + (istr.charAt(ilen) - '0') + (jstr.charAt(jlen) - '0');
if (cur >= 10) {
cur -= 10;
isadd = 1;
}else{
isadd = 0;
}
res = cur + res;
--ilen;
--jlen;
}
if (ilen >= 0) return res = solve(ilen, istr, res, isadd);
else if (jlen >= 0) return res = solve(jlen, jstr, res, isadd);
else return isadd == 1 ? "1" + res : res;
}
private String solve(int len, String num, String res, int isadd) {
int cur = 0;
while (len >= 0){
cur = isadd + (num.charAt(len) - '0');
if (cur >= 10) {
cur -= 10;
isadd = 1;
}else{
isadd = 0;
}
res = cur + res;
--len;
}
return isadd == 1 ? res = "1" + res : res;
}
}
其中在力扣的题解中发现一个也是使用递归但是转为数字的加法来处理的:
class Solution {
public boolean isAdditiveNumber(String num) {
if (num == null || num.length() < 3) {
return false;
}
char[] chars = num.toCharArray();
long pre1, pre2;
// 起始数字要满足:
// 1.开头不为0
// 2.长度不超过总长度的一半
for (int i = 0; i < num.length() / 2; i ++) {
if (chars[0] == '0' && i > 0) {
break;
}
pre1 = getNum(chars, 0, i);
for (int j = i+1; j < num.length(); j ++) {
if (chars[i+1] == '0' && j > i + 1) {
break;
}
pre2 = getNum(chars, i+1, j);
if (isAdditiveNumberCore(chars, j+1, pre1, pre2)) {
return true;
}
}
}
return false;
}
/**
* 前两个数相加等于第三个数,如果不符返回false
*/
private boolean isAdditiveNumberCore(char[] num, int start, long pre1, long pre2) {
long target = pre1 + pre2;
int length = String.valueOf(target).length();
if (getNum(num, start, start + length - 1) != target) {
return false;
}
start = start + length;
return start == num.length || isAdditiveNumberCore(num, start, pre2, target);
}
private long getNum(char[] chars, int start, int end) {
if (end >= chars.length) {
return -1;
}
long ans = 0;
for (int i = start; i <= end; i ++) {
ans *= 10;
ans += (chars[i] - '0');
}
return ans;
}
}