1. 题目来源
2. 题目解析
细节蛮多的一道题,首先字符串是 20 位,那么最多的分割线也只有 19 段,所以暴力枚举就是 2 19 = 524288 2^{19}=524288 219=524288 种情况。在此使用二进制枚举分割线的方式来解题。
细节:
- 至少存在一段,则从 1 开始枚举到
n-1
段,即枚举n-1
个分割线。 - 考虑本题有 20 位数字,那么使用
long long
的话最多是9e18
是 19 位数字,但是也可能会溢出,架不住全是 99…,所以得使用unsigned long long
是 20 位数字,刚刚可以满足要求了。貌似也是支持__int128
的。
记得最后得再次判断 if (x != last - 1) flag = false;
否则 "1234"
,就会被判成 true
,由于切的第一刀的时候 last=-1
所以就不能进入判断,后面都没有切故,234
被看成一段,但是 1
、234
是不满足要求的,但是由于没有这个判断,flag
没发生变化,就直接 return true;
了。故在只切一刀的条件下,这个条件的作用就很重要了。
后来思考了下,应该还对最后一个分割线有用,即最后一个区间和倒数第二个区间,不会在循环中进行判断,所以会在外面进行判断。诸如这种用法,在枚举区间问题中,还是很常见的,即最后一个区间可以枚举到,但是没在循环中判断,需要在外部判断。
- 时间复杂度: O ( 2 n ∗ n ) O(2^n*n) O(2n∗n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution {
public:
bool splitString(string s) {
int n = s.size();
for (int i = 1; i < 1 << n - 1; i ++ ) { // 二进制枚举所有的分割线情况,最多是 2^19=524288 种情况
bool flag = true;
unsigned long long last = -1, x = s[0] - '0'; // last 表示上一个数,x 表示当前数,第一个数一定属于第一段
for (int j = 0; j < n - 1; j ++ ) { // 枚举分割线
if (1 << j & i) { // 如果 j 是分割线的话,s[j+1] 属于下一区间,否则 s[j+1] 属于当前区间
if (last != -1 && x != last - 1) { // 不满足 last-1==x,则当前枚举的分割线不成立,看下一种即可
flag = false;
break;
}
last = x; // 满足 last-1==x,则当前分割线成立,s[j+1] 属于下一区间
x = s[j + 1] - '0';
} else { // 如果 j 不是分割线的话,s[j+1] 属于当前区间
x = x * 10 + s[j + 1] - '0';
}
}
if (x != last - 1) flag = false; // 如果最后一个区间不满足 x!=last-1,则该种分割方法不满足
if (flag) return true; // 如果有一种分割方法满足,则说明能满足题目要求,返回 true 即可
}
return false;
}
};