大家好,我是 @负雪明烛。👈👈 点击关注,优质题解不间断。

题目大意

给出一个字符串只包含 ID,求数组 942. 增减字符串匹配 - 图1的一个排列,使得:

  • 如果字符串的某个位置是 I,那么数组下一个数字是增加的;
  • 如果字符串的某个位置是 D,那么数组下一个数字是减小的。

用下面的图帮助理解,我用了题目中的两个例子s = "IDID""s = DDI"

根据图,我再解释一下s = "IDID"这个例子。

这个字符串的含义是让我们找到一个 增减增减的数组。数组的长度是 s的长度 942. 增减字符串匹配 - 图2,即 942. 增减字符串匹配 - 图3;数组中的数字必须是 942. 增减字符串匹配 - 图4942. 增减字符串匹配 - 图5 个数字,必须不重复、不遗漏。

直白点就是问我们,怎么把 942. 增减字符串匹配 - 图6排列成 增减增减的形状。

所以题目,给出了一种答案:942. 增减字符串匹配 - 图7这样。

有没有其他答案呢?也是有的,比如 942. 增减字符串匹配 - 图8也是 增减增减的形状。

题目也说了,只要是符合 s要求的任意一个排列,都是正确答案。所以上面两种排列都是对的。

解题方法

贪心算法

这个题其实有规律可循的,如果注意看题目给的例子,其实也是有规律的。

我们尝试进行分析,仍以s = "IDID"为例,即要把942. 增减字符串匹配 - 图9重新排列 :

  1. 假如 s当前的字符是 I,说明数组中的下一个数字比当前数字大
    1. 假如我们把数组中的当前安排成 942. 增减字符串匹配 - 图10,那就完犊子了,因为不存在比 942. 增减字符串匹配 - 图11更大的数字了。
    2. 所以我们当遇到 I的时候,我们最好的选择是当前能放的最小数字。这样,无论下一个数字放谁,都会比当前数字大。都能符合 I
  2. 假如 s当前的字符是 D,说明数组中的下一个数字比当前数字小
    1. 假如我们把数组中的当前安排成 942. 增减字符串匹配 - 图12,那就完犊子了,因为不存在比 942. 增减字符串匹配 - 图13更小的数字了。
    2. 所以我们当遇到 D的时候,我们最好的选择是当前能放的最大数字。这样,无论下一个数字放谁,都会比当前数字小。都能符合 D

所以对于s = "IDID"而言,待重新排序的数组是 942. 增减字符串匹配 - 图14。我们对 s进行遍历:

  1. s第一个的字符是 I,当前候选集是 $[0,1,2,3,4]$,我们选择最小值 942. 增减字符串匹配 - 图15(无论下一个数字放谁,都比 942. 增减字符串匹配 - 图16大,所以都是增加);此时结果数组为 $[0]$;
  2. s第二个的字符是 D,当前候选集是 $[1,2,3,4]$,我们选择最大值 942. 增减字符串匹配 - 图17(无论下一个数字放谁,都比 942. 增减字符串匹配 - 图18小,所以一定减小);此时结果数组为 $[0, 4]$;
  3. s第三个的字符是 I,当前候选集是 $[1,2,3]$,我们选择最大值 942. 增减字符串匹配 - 图19( 无论下一个数字放谁,都比 942. 增减字符串匹配 - 图20大,所以都是增加);此时结果数组为 $[0, 4, 1]$;
  4. s第四个的字符是 D,当前候选集是 $[2,3]$,我们选择最大值 942. 增减字符串匹配 - 图21(无论下一个数字放谁,都比 942. 增减字符串匹配 - 图22小,所以一定减小);此时结果数组为 $[0, 4, 1, 3]$;
  5. s遍历完成,候选集还剩 $[2]$, 我们把最后这个数字放在结果数组中,结果数组为 $[0, 4, 1, 3, 2]$;

总之,就是一个贪心策略,遇到 I就选剩余候选集的最小值,遇到 D就选剩余候选集的最大值。

下面的代码中,为了保存当前的最小值和最大值,分别使用了两个变量 nind

代码如下:

  1. class Solution:
  2. def diStringMatch(self, S):
  3. """
  4. :type S: str
  5. :rtype: List[int]
  6. """
  7. N = len(S)
  8. ni, nd = 0, N
  9. res = []
  10. for s in S:
  11. if s == "I":
  12. res.append(ni)
  13. ni += 1
  14. else:
  15. res.append(nd)
  16. nd -= 1
  17. res.append(ni)
  18. return res
  1. class Solution {
  2. public:
  3. vector<int> diStringMatch(string s) {
  4. int ni = 0;
  5. int nd = s.size();
  6. vector<int> res;
  7. for (char c : s) {
  8. if (c == 'I') {
  9. res.push_back(ni);
  10. ni ++;
  11. } else {
  12. res.push_back(nd);
  13. nd --;
  14. }
  15. }
  16. res.push_back(ni);
  17. return res;
  18. }
  19. };
  1. class Solution {
  2. public int[] diStringMatch(String s) {
  3. int[] res = new int[s.length() + 1];
  4. int ni = 0;
  5. int nd = s.length();
  6. for (int i = 0; i < s.length(); ++i) {
  7. if (s.charAt(i) == 'I') {
  8. res[i] = ni;
  9. ni ++;
  10. } else {
  11. res[i] = nd;
  12. nd --;
  13. }
  14. }
  15. res[s.length()] = ni;
  16. return res;
  17. }
  18. }

复杂度

  • 时间复杂度:$O(N)$
  • 空间复杂度:$O(1)$

    总结

  1. 这种找规律题目,不妨按照题目给出的示例,自己去琢磨一下。

我是 @负雪明烛 ,刷算法题 1000 多道,写了 1000 多篇算法题解,收获阅读量 300 万。
关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。