增减字符串匹配

标签: 贪心 数组 双指针 字符串

难度: Easy

由范围 [0,n] 内所有整数组成的 n + 1 个整数的排列序列可以表示为长度为 n 的字符串 s ,其中:

  • 如果 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 <= 105
  • s 只包含字符 "I" 或 "D"

Submission

运行时间: 19 ms

内存: 16.7 MB

class Solution:
    def diStringMatch(self, s: str) -> List[int]:
        n = down = len(s)
        up = index = 0
        res = [0] * (n + 1)
        for c in s:
            if c == 'I':
                res[index] = up
                up += 1
            else :
                res[index] = down
                down -= 1
            index += 1
        res[n] = up
        return res
                  

Explain

该题解采用了两个指针策略,up 和 down。up 从0开始递增,代表在排列中下一个最小的数字;down 从n开始递减,代表在排列中下一个最大的数字。遍历给定的字符串s,根据每个字符是'I'(递增)还是'D'(递减),从up或down取值填充结果数组。最后一个位置用剩余的up或down填充,保证所有数字都被使用。

时间复杂度: O(n)

空间复杂度: O(n)

# Python代码注释

class Solution:
    def diStringMatch(self, s: str) -> List[int]:
        n = down = len(s)  # n为字符串长度,down初始化为n
        up = index = 0  # up从0开始,index用于记录当前填充的数组索引
        res = [0] * (n + 1)  # 初始化结果数组,长度为n+1
        for c in s:  # 遍历字符串
            if c == 'I':  # 如果字符为'I'
                res[index] = up  # 在当前位置放置up代表的最小值
                up += 1  # 更新up
            else:  # 如果字符为'D'
                res[index] = down  # 在当前位置放置down代表的最大值
                down -= 1  # 更新down
            index += 1  # 移动到下一个索引
        res[n] = up  # 填充最后一个元素
        return res

Explore

算法设计的初衷是为了根据字符串s中的'I'和'D'来构建一个满足条件的排列。每次选择更新up或down是为了能够清晰地表示递增或递减的关系。若同时更新两个指针,则会失去这种一致的递增或递减的映射关系,导致无法直接通过'I'或'D'来决定具体的数值。此外,这种单一指针更新策略简化了逻辑,并确保了数组中的每个数都是唯一且适当的。

在字符串s处理完毕后,下一个待填充的数组位置是最后一个元素。这时,up指针和down指针会恰好指向同一个数值。这是因为up始终指向未被使用的最小值,而down指向未被使用的最大值,当遍历完字符串s后,剩下的唯一未被使用的数恰好是这两个指针相遇的地方。因此,可以保证剩余的up或down的值可以直接用于填充数组的最后一个位置,同时确保所有数字从0到n都被使用一次,无遗漏。

如果字符串s全是'I',则表示数组需要完全递增,此时算法会从0开始依次递增至n。如果字符串s全是'D',则表示数组需要完全递减,此时算法会从n开始依次递减至0。这种情况下算法的基本逻辑不变,且没有必要进行特殊的处理或优化,因为该算法已经以最简洁的方式处理了这些极端情况。这也体现了算法的通用性和效率。

这样的设计是为了直接映射字符'I'和'D'的字面意义——'I'代表递增,'D'代表递减。通过将较小的数赋给'I',可以确保数字序列在遇到'I'时呈现递增趋势;相反,将较大的数赋给'D'可以确保数字序列在遇到'D'时呈现递减趋势。这种处理逻辑简化了算法的实现,并且直观上与问题描述保持一致,易于理解和验证。此外,这种方法也避免了额外的计算或调整,提高了算法的效率。