相邻重复字母删除问题 | 豆包MarsCode AI刷题

152 阅读4分钟

问题描述

小M拿到了一个由小写字母组成的字符串 s。她发现可以进行一种操作:选择两个相邻且相同的字母并删除它们。她可以在 s 上反复进行此操作,直到无法再删除任何字母。

请返回最终处理完所有重复项删除操作后的字符串。可以保证返回的答案是唯一的。


测试样例

样例1:

输入:s = "abbaca"
输出:'ca'

样例2:

输入:s = "azxxzy"
输出:'ay'

样例3:

输入:s = "a"
输出:'a'

题目分析

这道题的核心是通过删除相邻且相同的字母,反复操作直到字符串中不再有可以删除的相邻相同字母,并返回最终结果字符串。
题目保证结果是唯一的,这意味着只要按规则反复执行操作,最后的字符串不会因操作顺序不同而改变。

解题思路

1. 使用栈解决问题的原因

  • 栈(Stack)是一个后进先出的数据结构,非常适合解决“相邻重复字符”的问题。

  • 我们可以利用栈检查字符串的相邻字符:

    • 如果当前字符与栈顶字符相同,则将栈顶字符弹出(删除一对相邻字符)。
    • 如果不同,则将当前字符压入栈中。

2. 消除相邻重复的逻辑

  • 当遍历完整个字符串后,栈中存放的字符就是处理后剩下的字符。
  • 栈的后进先出特性意味着结果字符的顺序需要反转。

具体实现

import java.util.Stack;

public class Main {
    public static String solution(String s) {
        Stack<Character> stack = new Stack<>();

        for (char c : s.toCharArray()) {
            // 如果栈不为空且栈顶元素与当前字符相同,弹出栈顶元素
            if (!stack.isEmpty() && stack.peek() == c) {
                stack.pop();
            } else {
                // 否则将当前字符推入栈中
                stack.push(c);
            }
        }

        // 构建结果字符串
        StringBuilder result = new StringBuilder();
        while (!stack.isEmpty()) {
            result.append(stack.pop());
        }

        // 由于栈是后进先出,反转结果字符串
        return result.reverse().toString();
    }

    public static void main(String[] args) {
        System.out.println(solution("abbaca").equals("ca")); // 输出: true
        System.out.println(solution("azxxzy").equals("ay")); // 输出: true
        System.out.println(solution("a").equals("a"));       // 输出: true
    }
}

代码解析

主函数:solution(String s)

  1. 初始化栈

    Stack<Character> stack = new Stack<>();
    

    创建一个栈来存储字符。

  2. 遍历字符串

    for (char c : s.toCharArray()) {
    

    将字符串 s 转为字符数组,逐一处理字符 c

  3. 判断栈顶字符是否与当前字符相同

    if (!stack.isEmpty() && stack.peek() == c) {
        stack.pop(); // 栈顶元素与当前字符相同,删除
    } else {
        stack.push(c); // 不同则压入栈中
    }
    
    • 如果栈非空且栈顶字符与当前字符相同,则弹出栈顶字符,表示移除这对重复字符。
    • 否则,将当前字符压入栈,表示它没有找到重复的相邻字符。
  4. 构建结果字符串

    StringBuilder result = new StringBuilder();
    while (!stack.isEmpty()) {
        result.append(stack.pop());
    }
    return result.reverse().toString();
    
    • 将栈中剩余的字符弹出并拼接成字符串。
    • 由于栈是后进先出的,最终结果需要反转。

测试样例

样例1:s = "abbaca"

  1. 初始字符串:abbaca

  2. 遍历并执行操作:

    • a 压栈,栈:[a]
    • b 压栈,栈:[a, b]
    • 再次遇到 b,弹出栈顶 b,栈:[a]
    • a 与栈顶 a 相同,弹出栈顶,栈:[]
    • c 压栈,栈:[c]
    • a 压栈,栈:[c, a]
  3. 栈中剩余字符为 ca(反转后仍为 ca)。

样例2:s = "azxxzy"

  1. 初始字符串:azxxzy

  2. 遍历并执行操作:

    • a 压栈,栈:[a]
    • z 压栈,栈:[a, z]
    • x 压栈,栈:[a, z, x]
    • 再次遇到 x,弹出栈顶 x,栈:[a, z]
    • z 与栈顶 z 相同,弹出栈顶,栈:[a]
    • y 压栈,栈:[a, y]
  3. 栈中剩余字符为 ay(反转后仍为 ay)。

样例3:s = "a"

  1. 初始字符串:a

  2. 遍历并执行操作:

    • a 压栈,栈:[a]
  3. 栈中剩余字符为 a

时间与空间复杂度分析

  1. 时间复杂度:

    • 遍历字符串的时间复杂度为 O(n)O(n)O(n),其中 nnn 是字符串的长度。
    • 栈操作(入栈和出栈)在单个字符上执行一次,总体仍为 O(n)O(n)O(n)。
    • 因此,总时间复杂度为 O(n)O(n)O(n)。
  2. 空间复杂度:

    • 最坏情况下,栈中可能存储字符串的所有字符,空间复杂度为 O(n)O(n)O(n)。
    • 但在大多数情况下,栈中的字符少于输入字符数量,实际空间利用更优。

总结

这道题通过使用栈简洁地解决了重复字符消除问题,代码逻辑清晰,时间和空间复杂度都在合理范围内,是处理类似问题的经典方法。