Hard
题目描述
给你一个字符串 message 和一个正整数 limit。
你必须根据 limit 将 message 拆分为一个或多个部分。每个结果部分都应该有后缀 "<a/b>",其中 "b" 要替换为总部分数,"a" 要替换为部分的索引,从 1 开始直到 b。此外,每个结果部分(包括其后缀)的长度应该等于 limit,除了最后一个部分的长度最多为 limit。
结果部分应该这样形成:当移除它们的后缀并按顺序连接时,应该等于 message。此外,结果应该包含尽可能少的部分。
返回 message 将被拆分成的部分作为字符串数组。如果无法按要求拆分 message,则返回空数组。
示例 1:
输入:message = "this is really a very awesome message", limit = 9
输出:["thi<1/14>","s i<2/14>","s r<3/14>","eal<4/14>","ly <5/14>","a v<6/14>","ery<7/14>"," aw<8/14>","eso<9/14>","me<10/14>"," m<11/14>","es<12/14>","sa<13/14>","ge<14/14>"]
解释:
前 9 个部分每个从 message 开头取 3 个字符。
接下来的 5 个部分每个取 2 个字符来完成 message 的拆分。
在这个例子中,每个部分包括最后一个都有长度 9。
可以证明不可能将 message 拆分成少于 14 个部分。
示例 2:
输入:message = "short message", limit = 15
输出:["short mess<1/2>","age<2/2>"]
解释:
在给定的约束下,字符串可以拆分为两部分:
- 第一部分包含前 10 个字符,长度为 15。
- 下一部分包含最后 3 个字符,长度为 8。
约束条件:
1 <= message.length <= 10^4message只包含小写英文字母和' '1 <= limit <= 10^4
解题思路
这道题的核心是确定拆分的总部分数,然后验证能否按此部分数成功拆分。
解题思路:
枚举总部分数:从1开始枚举可能的总部分数
parts,对于每个部分数检查是否能成功拆分。计算后缀长度:对于总部分数
parts,每个部分的后缀格式为<i/parts>,其中i是部分索引。后缀长度为len(str(i)) + len(str(parts)) + 3(包括<、/、>)。验证可行性:
- 计算每个部分可用于存放消息内容的空间:
limit - 后缀长度 - 如果某个部分的可用空间 ≤ 0,则当前部分数不可行
- 计算所有部分的总可用空间,如果小于消息长度,则不可行
- 计算每个部分可用于存放消息内容的空间:
构造结果:一旦找到最小可行的部分数,按顺序构造每个部分,将消息内容和对应后缀组合。
关键优化:
- 按部分数从小到大枚举,找到第一个可行解就是最优解
- 提前检查总可用空间,避免不必要的计算
时间复杂度主要取决于总部分数的上界,在最坏情况下约为 O(√n),其中 n 为消息长度。
代码实现
class Solution {
public:
vector<string> splitMessage(string message, int limit) {
int n = message.length();
for (int parts = 1; parts <= n; parts++) {
// 计算是否可以用 parts 个部分来分割
int totalAvailable = 0;
bool valid = true;
for (int i = 1; i <= parts; i++) {
int suffixLen = to_string(i).length() + to_string(parts).length() + 3; // <i/parts>
int available = limit - suffixLen;
if (available <= 0) {
valid = false;
break;
}
totalAvailable += available;
}
if (!valid || totalAvailable < n) continue;
// 构造结果
vector<string> result;
int pos = 0;
for (int i = 1; i <= parts; i++) {
string suffix = "<" + to_string(i) + "/" + to_string(parts) + ">";
int available = limit - suffix.length();
string part = message.substr(pos, min(available, n - pos)) + suffix;
result.push_back(part);
pos += available;
}
return result;
}
return {};
}
};
class Solution:
def splitMessage(self, message: str, limit: int) -> List[str]:
n = len(message)
for parts in range(1, n + 1):
# 计算是否可以用 parts 个部分来分割
total_available = 0
valid = True
for i in range(1, parts + 1):
suffix_len = len(str(i)) + len(str(parts)) + 3 # <i/parts>
available = limit - suffix_len
if available <= 0:
valid = False
break
total_available += available
if not valid or total_available < n:
continue
# 构造结果
result = []
pos = 0
for i in range(1, parts + 1):
suffix = f"<{i}/{parts}>"
available = limit - len(suffix)
part = message[pos:pos + min(available, n - pos)] + suffix
result.append(part)
pos += available
return result
return []
public class Solution {
public string[] SplitMessage(string message, int limit) {
int n = message.Length;
for (int parts = 1; parts <= n; parts++) {
// 计算是否可以用 parts 个部分来分割
int totalAvailable = 0;
bool valid = true;
for (int i = 1; i <= parts; i++) {
int suffixLen = i.ToString().Length + parts.ToString().Length + 3; // <i/parts>
int available = limit - suffixLen;
if (available <= 0) {
valid = false;
break;
}
totalAvailable += available;
}
if (!valid || totalAvailable < n) continue;
// 构造结果
var result = new List<string>();
int pos = 0;
for (int i = 1; i <= parts; i++) {
string suffix = $"<{i}/{parts}>";
int available = limit - suffix.Length;
string part = message.Substring(pos, Math.Min(available, n - pos)) + suffix;
result.Add(part);
pos += available;
}
return result.ToArray();
}
return new string[0];
}
}
var splitMessage = function(message, limit) {
const n = message.length;
for (let parts = 1; parts <= n; parts++) {
// 计算是否可以用 parts 个部分来分割
let totalAvailable = 0;
let valid = true;
for (let i = 1; i <= parts; i++) {
const suffixLen = i.toString().length + parts.toString().length + 3; // <i/parts>
const available = limit - suffixLen;
if (available <= 0) {
valid = false;
break;
}
totalAvailable += available;
}
if (!valid || totalAvailable < n) continue;
// 构造结果
const result = [];
let pos = 0;
for (let i = 1; i <= parts; i++) {
const suffix = `<${i}/${parts}>`;
const available = limit - suffix.length;
const part = message.substring(pos, pos + Math.min(available, n - pos)) + suffix;
result.push(part);
pos += available;
}
return result;
}
return [];
};
复杂度分析
| 复杂度类型 | 值 |
|---|---|
| 时间复杂度 | O(k²) 其中 k 是最终的部分数,最坏情况下 k = O(√n) |
| 空间复杂度 | O(k) 用于存储结果数组 |
相关题目
. Text Justification (Hard)
. Search a 2D Matrix (Medium)
. Sentence Screen Fitting (Medium)