Easy
题目描述
长度为 n 的字符串 s 可以表示一个由 n+1 个整数组成的排列 perm,其中排列包含范围 [0, n] 内的所有整数,表示规则如下:
- 如果 perm[i] < perm[i + 1],那么 s[i] == ‘I’
- 如果 perm[i] > perm[i + 1],那么 s[i] == ‘D’
给定一个字符串 s,请重构排列 perm 并返回它。如果有多个有效的排列 perm,返回其中任意一个即可。
示例 1:
输入:s = "IDID"
输出:[0,4,1,3,2]
示例 2:
输入:s = "III"
输出:[0,1,2,3]
示例 3:
输入:s = "DDI"
输出:[3,2,0,1]
提示:
- 1 <= s.length <= 10^5
- s[i] 是 ‘I’ 或 ‘D’
解题思路
解题思路
这是一个贪心算法的经典应用。我们需要构造一个包含 0 到 n 的排列,使其满足给定的 DI 字符串条件。
核心观察:
- 当遇到 ‘I’ 时,我们希望当前位置的数字尽可能小,这样下一个位置就有更多选择空间
- 当遇到 ‘D’ 时,我们希望当前位置的数字尽可能大,这样下一个位置就有更多选择空间
贪心策略:
维护两个指针 low 和 high,分别指向当前可用数字的最小值和最大值:
- 遇到 ‘I’:使用
low,然后low++ - 遇到 ‘D’:使用
high,然后high-- - 最后一个位置:使用剩余的那个数字(此时
low == high)
这种方法保证了每一步都做出最优选择,使得后续的选择空间最大化。
算法步骤:
- 初始化
low = 0,high = n - 遍历字符串 s,根据当前字符选择使用 low 还是 high
- 添加最后一个数字
时间复杂度 O(n),空间复杂度 O(1)(不计算结果数组)。
代码实现
class Solution {
public:
vector<int> diStringMatch(string s) {
int n = s.length();
vector<int> result;
int low = 0, high = n;
for (char c : s) {
if (c == 'I') {
result.push_back(low++);
} else {
result.push_back(high--);
}
}
result.push_back(low);
return result;
}
};
class Solution:
def diStringMatch(self, s: str) -> List[int]:
n = len(s)
result = []
low, high = 0, n
for c in s:
if c == 'I':
result.append(low)
low += 1
else:
result.append(high)
high -= 1
result.append(low)
return result
public class Solution {
public int[] DiStringMatch(string s) {
int n = s.Length;
int[] result = new int[n + 1];
int low = 0, high = n;
int index = 0;
foreach (char c in s) {
if (c == 'I') {
result[index++] = low++;
} else {
result[index++] = high--;
}
}
result[index] = low;
return result;
}
}
var diStringMatch = function(s) {
let low = 0, high = s.length;
let result = [];
for (let i = 0; i < s.length; i++) {
if (s[i] === 'I') {
result.push(low++);
} else {
result.push(high--);
}
}
result.push(low);
return result;
};
复杂度分析
| 复杂度类型 | 值 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
其中 n 是字符串 s 的长度。时间复杂度为 O(n) 因为需要遍历一次字符串;空间复杂度为 O(1) 不考虑结果数组的存储空间。