题目来源:https://leetcode-cn.com/problems/additive-number/
大致题意:
给一个由数字组成的字符串,将其分为大于等于 3 个的子串,从第三个开始,每个子串都等于其前两个子串转化为整数的和。如果子串长度大于 1,那么不能以 0 开头
如果字符串可以满足上述条件,那么返回 true,否则返回 false
思路
枚举前两个子串的所有可能性
根据每次枚举的前两个子串,算出第三个子串,然后判断是否与实际上的第三个子串相等,若相等,可以根据第三个子串是否为字符串末尾来进行下一步操作:若是末尾,那么表示满足条件,返回 true;否则,将第二个子串和第三个子串相加,重复这个操作
枚举
具体枚举时,不用将两个子串都枚举,只需要枚举第二个子串即可:
- 若第二个子串开始位置为 secondStart,结束位置为 secondEnd
- 那么第一个子串开始位置默认为 0,结束位置为 secondStart - 1
代码
public class IsAdditiveNumber {
public boolean isAdditiveNumber(String num) {
int n = num.length();
if (n < 3) {
return false;
}
// 枚举第二个子串的起始位置
for (int secondStart = 1; secondStart < n - 1; secondStart++) {
// 如果第一个子串以 0 开头,那么当其长度不能大于 1
if (num.charAt(0) == '0' && secondStart != 1) {
continue;
}
for (int secondEnd = secondStart; secondEnd < n - 1; secondEnd++) {
// 如果第二个子串以 0 开头,那么当其长度不能大于 1
if (num.charAt(secondStart) == '0' && secondEnd != secondStart) {
continue;
}
// 本轮枚举符合条件,直接返回结果
if (checkValid(secondStart, secondEnd, num)) {
return true;
}
}
}
return false;
}
// 检查本轮枚举是否可以让字符串符合条件
public boolean checkValid(int secondStart, int secondEnd, String num) {
// 获取第一个子串的起始位置
int firstStart = 0;
int firstEnd = secondStart - 1;
int n = num.length();
while (secondEnd < n - 1) {
// 计算出下一个子串
String third = stringAdd(firstStart, firstEnd, secondStart, secondEnd, num);
// 计算出下一个子串的起始位置
int thirdStart = secondEnd + 1;
int thirdEnd = secondEnd + third.length();
// 如果结束位置大于字符串长度,或者算出的子串和实际不匹配,表示本轮枚举不符合条件
if (thirdEnd >= n || !third.equals(num.substring(thirdStart, thirdEnd + 1))) {
return false;
}
// 如果结束位置为字符串末尾,那么表示计算完毕,本轮枚举符合条件
if (thirdEnd == n - 1) {
return true;
}
// 字符串还有剩余,需要继续计算剩余位置是否符合条件
// 更新要求和的两个子串
firstStart = secondStart;
firstEnd = secondEnd;
secondStart = thirdStart;
secondEnd = thirdEnd;
}
return false;
}
// 字符串加法
/**
*
* @param firstStart 第一个子串开头位置
* @param firstEnd 第一个子串结尾位置
* @param secondStart 第二个子串开头位置
* @param secondEnd 第二个子串结尾位置
* @param num 原字符串
* @return 两个子串的和
*/
public String stringAdd(int firstStart, int firstEnd, int secondStart, int secondEnd, String num) {
StringBuffer sum = new StringBuffer();
// 进位
int carry = 0;
// 从低位到高位开始计算
// 当还有一个子串未遍历完,或者还有进位时,需要继续运算
while (firstEnd >= firstStart || secondEnd >= secondStart || carry != 0) {
int cur = carry;
// 第一个子串未遍历完
if (firstEnd >= firstStart) {
cur += num.charAt(firstEnd--) - '0';
}
// 第二子串未遍历完
if (secondEnd >= secondStart) {
cur += num.charAt(secondEnd--) - '0';
}
// 更新进位
carry = cur / 10;
// 取出当前位结果
sum.append(cur % 10);
}
// 因为是从低位到高位算的,需要取反
sum.reverse();
return sum.toString();
}
}