Medium
题目描述
给你一个二进制字符串 s,你需要将字符串分割成一个或多个子字符串,使得每个子字符串都是美丽的。
如果一个字符串满足以下条件,则认为它是美丽的:
- 它不包含前导零。
- 它是某个 5 的幂的二进制表示。
返回这样分割的最少子字符串数目。如果无法将字符串 s 分割成美丽的子字符串,返回 -1。
子字符串是字符串中的一个连续字符序列。
示例 1:
输入:s = "1011"
输出:2
解释:我们可以将给定的字符串分割为 ["101", "1"]。
- 字符串 "101" 不包含前导零,是整数 5^1 = 5 的二进制表示。
- 字符串 "1" 不包含前导零,是整数 5^0 = 1 的二进制表示。
可以证明 2 是 s 可以分割成的美丽子字符串的最少数目。
示例 2:
输入:s = "111"
输出:3
解释:我们可以将给定的字符串分割为 ["1", "1", "1"]。
- 字符串 "1" 不包含前导零,是整数 5^0 = 1 的二进制表示。
可以证明 3 是 s 可以分割成的美丽子字符串的最少数目。
示例 3:
输入:s = "0"
输出:-1
解释:我们无法将给定的字符串分割成美丽的子字符串。
提示:
1 <= s.length <= 15s[i]是 ‘0’ 或 ‘1’
解题思路
这道题需要将二进制字符串分割成最少的美丽子字符串,其中美丽字符串必须是5的幂的二进制表示且无前导零。
解题思路:
预处理5的幂:由于字符串长度最多15位,我们可以预先计算所有可能的5的幂的二进制表示并存储在集合中,便于快速查找。
动态规划:使用dp[i]表示从位置i到字符串末尾的最少分割数。状态转移方程为:
- 对于每个位置i,尝试所有可能的子字符串s[i:j]
- 如果s[i:j]是美丽字符串,则dp[i] = min(dp[i], 1 + dp[j])
回溯法:也可以使用递归回溯,从前往后尝试所有可能的分割点,记录最小分割数。
美丽字符串判断:
- 不能以0开头(除非就是"0"但"0"不是5的幂)
- 必须是5的幂的二进制表示
由于数据规模较小(长度≤15),两种方法都可行。这里采用动态规划方法,从后往前计算,避免了递归的开销。
推荐解法:动态规划,时间复杂度较低且思路清晰。
代码实现
class Solution {
public:
int minimumBeautifulSubstrings(string s) {
int n = s.length();
// 预计算所有可能的5的幂的二进制表示
unordered_set<string> powers;
long long power = 1;
while (power <= (1LL << 15)) {
string binary = "";
long long temp = power;
while (temp > 0) {
binary = char('0' + temp % 2) + binary;
temp /= 2;
}
powers.insert(binary);
power *= 5;
}
// dp[i] 表示从位置i到末尾的最少分割数
vector<int> dp(n + 1, INT_MAX);
dp[n] = 0;
for (int i = n - 1; i >= 0; i--) {
if (s[i] == '0') continue; // 不能以0开头
for (int j = i + 1; j <= n; j++) {
string substr = s.substr(i, j - i);
if (powers.count(substr) && dp[j] != INT_MAX) {
dp[i] = min(dp[i], 1 + dp[j]);
}
}
}
return dp[0] == INT_MAX ? -1 : dp[0];
}
};
class Solution:
def minimumBeautifulSubstrings(self, s: str) -> int:
n = len(s)
# 预计算所有可能的5的幂的二进制表示
powers = set()
power = 1
while power <= (1 << 15):
powers.add(bin(power)[2:]) # 去掉'0b'前缀
power *= 5
# dp[i] 表示从位置i到末尾的最少分割数
dp = [float('inf')] * (n + 1)
dp[n] = 0
for i in range(n - 1, -1, -1):
if s[i] == '0':
continue # 不能以0开头
for j in range(i + 1, n + 1):
substr = s[i:j]
if substr in powers and dp[j] != float('inf'):
dp[i] = min(dp[i], 1 + dp[j])
return dp[0] if dp[0] != float('inf') else -1
public class Solution {
public int MinimumBeautifulSubstrings(string s) {
int n = s.Length;
// 预计算所有可能的5的幂的二进制表示
var powers = new HashSet<string>();
long power = 1;
while (power <= (1L << 15)) {
powers.Add(Convert.ToString(power, 2));
power *= 5;
}
// dp[i] 表示从位置i到末尾的最少分割数
int[] dp = new int[n + 1];
Array.Fill(dp, int.MaxValue);
dp[n] = 0;
for (int i = n - 1; i >= 0; i--) {
if (s[i] == '0') continue; // 不能以0开头
for (int j = i + 1; j <= n; j++) {
string substr = s.Substring(i, j - i);
if (powers.Contains(substr) && dp[j] != int.MaxValue) {
dp[i] = Math.Min(dp[i], 1 + dp[j]);
}
}
}
return dp[0] == int.MaxValue ? -1 : dp[0];
}
}
var minimumBeautifulSubstrings = function(s) {
const n = s.length;
// 预计算所有可能的5的幂的二进制表示
const powers = new Set();
let power = 1;
while (power <= (1 << 15)) {
powers.add(power.toString(2));
power *= 5;
}
// dp[i] 表示从位置i到末尾的最少分割数
const dp = new Array(n + 1).fill(Infinity);
dp[n] = 0;
for (let i = n - 1; i >= 0; i--) {
if (s[i]
复杂度分析
| 复杂度类型 | 动态规划解法 |
|---|---|
| 时间复杂度 | O(n²) |
| 空间复杂度 | O(n + k) |
其中 n 是字符串长度,k 是预计算的5的幂的数量(约为 log₅(2¹⁵) ≈ 11)。