LeetCode 3170. 删除星号以后字典序最小的字符串

3170. 删除星号以后字典序最小的字符串

给你一个字符串 s 。它可能包含任意数量的 '*' 字符。你的任务是删除所有的 '*' 字符。

当字符串还存在至少一个 '*' 字符时,你可以执行以下操作:

  • 删除最左边的 '*' 字符,同时删除该星号字符左边一个字典序 最小 的字符。如果有多个字典序最小的字符,你可以删除它们中的任意一个。

请你返回删除所有 '*' 字符以后,剩余字符连接而成的 字典序最小 的字符串。

示例 1:

输入:s = "aaba*"

输出:"aab"

解释:删除 '*' 号和它左边的其中一个 'a' 字符。如果我们选择删除 s[3] ,s 字典序最小。

示例 2:

输入:s = "abc"

输出:"abc"

解释:字符串中没有 '*' 字符。

提示:

  • 1 <= s.length <= 10^5
  • s 只含有小写英文字母和 '*' 字符。
  • 输入保证操作可以删除所有的 '*' 字符。

解法1:栈模拟

这个问题实际上是一个字符串处理问题,我们需要删除字符串中所有的''字符,并保证删除操作后剩余的字符串字典序最小。这里的关键是如何有效处理  " * "  字符,以及如何选择删除哪些字符以保证结果的字典序最小。

核心思想

我们采取的核心思想是:使用栈结构来维护每个字符的索引,当遇到" * " 时,我们从栈中弹出索引最小的字符。这样做的好处是,我们可以保证每次删除操作后,剩余的字符中字典序最小的字符被保留下来。

算法逻辑

  1. 初始化26个栈,分别对应26个小写字母。每个栈用于存储对应字符的索引。
  2. 遍历字符串,对于每个字符执行以下操作:
    • 如果字符不是'*',则将其索引推入对应字符的栈中。
    • 如果字符是'*',则从26个栈中找到非空的第一个栈,并弹出其栈顶元素(即索引),这意味着我们“删除”了该索引处的字符。
  3. 重复步骤2,直到字符串中的所有'*'都被处理。
  4. 将所有栈中的索引进行排序,然后根据这些索引从原字符串中取出字符,拼接成最终的字符串。

核心思路:由于要去掉最小的字母,为了让字典序尽量小,相比去掉前面的字母,去掉后面的字母更好。

  1. 从左到右遍历 s,用 26 个栈模拟。
  2. 第 i 个栈维护第 i 个小写字母的下标。
  3. 遇到 * 时,弹出第一个非空栈的栈顶下标。
  4. 最后把所有栈顶下标对应的字母组合起来,即为答案。

Java版:

class Solution {
    public String clearStars(String s) {
        char[] arr = s.toCharArray();
        List<Integer>[] marks = new ArrayList[26];
        Arrays.setAll(marks, i -> new ArrayList<>());
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] != '*') {
                marks[arr[i] - 'a'].add(i);
                continue;
            }

            for (List<Integer> p: marks) {
                if (!p.isEmpty()) {
                    p.remove(p.size() - 1);
                    break;
                }
            }
        }

        List<Integer> idx = new ArrayList<>();
        for (List<Integer> p: marks) {
            idx.addAll(p);
        }
        Collections.sort(idx);

        StringBuilder ans = new StringBuilder(idx.size());
        for (int i: idx) {
            ans.append(arr[i]);
        }
        return ans.toString();
    }
}

Python3版:

itertools.chain.from_iterable() 是 Python itertools 模块中的一个函数,用于将嵌套的可迭代对象展平(flatten),生成一个单一的迭代器。这个函数特别适用于处理包含多个子可迭代对象的可迭代对象,例如嵌套列表或列表的列表。

应用场景

  • 数据处理和转换:在处理嵌套数据结构时,可以使用 itertools.chain.from_iterable 将其展平,便于进一步处理和分析。
  • 合并多个子集合:在需要将多个子集合合并为一个整体集合时,可以使用该函数简化操作。
  • 读取和处理文件:在处理包含多个段落或多行的文本文件时,可以使用该函数将其展平,便于逐行处理。
import itertools
class Solution:
    def clearStars(self, s: str) -> str:
        marks = [[] for _ in range(26)]
        for i, ch in enumerate(s):
            if ch != '*':
                marks[ord(ch) - ord('a')].append(i)
                continue 
            for p in marks:
                if len(p) > 0:
                    p.pop()
                    break 
        
        return "".join([s[i] for i in sorted(itertools.chain.from_iterable(marks))])

复杂度分析

  • 时间复杂度:O(n∣Σ∣),其中 n 是 s 的长度,∣Σ∣ 为字符集合的大小,本题字符均为小写字母,所以 ∣Σ∣=26。
  • 空间复杂度:O(n∣Σ∣)。

解法2:

把要删除的字母改成 *,然后去掉所有 * 号。

Java版:

class Solution {
    public String clearStars(String s) {
        char[] arr = s.toCharArray();
        List<Integer>[] marks = new ArrayList[26];
        Arrays.setAll(marks, i -> new ArrayList<>());
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] != '*') {
                marks[arr[i] - 'a'].add(i);
                continue;
            }
            for (List<Integer> p: marks) {
                if (!p.isEmpty()) {
                    arr[p.remove(p.size() - 1)] = '*';
                    break;
                }
            }
        }

        StringBuilder ans = new StringBuilder();
        for (char c: arr) {
            if (c != '*') {
                ans.append(c);
            }
        }
        return ans.toString();
    }
}

Python3版:

class Solution:
    def clearStars(self, s: str) -> str:
        s = list(s)
        marks = [[] for _ in range(26)]
        for i, ch in enumerate(s):
            if ch != '*':
                marks[ord(ch) - ord('a')].append(i)
                continue 
            for p in marks:
                if p:
                    # pop()就是删除最后一个元素
                    s[p.pop()] = '*'
                    break 
        return ''.join(c for c in s if c != '*')

复杂度分析

  • 时间复杂度:O(n∣Σ∣),其中 n 是 s 的长度,∣Σ∣ 为字符集合的大小,本题字符均为小写字母,所以 ∣Σ∣=26。
  • 空间复杂度:O(n + ∣Σ∣)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值