删除相邻重复字符

213 阅读4分钟

学习笔记:删除相邻重复字符

一、题目概述

给定一个字符串 ss,可以进行以下操作:选择两个相邻且相同的字符并删除它们。需要反复进行此操作,直到无法再删除任何字符。任务是返回最终删除重复项后的字符串。

示例

样例1:

输入:s = "abbaca"

输出:"ca"

样例2:

输入:s = "azxxzy"

输出:"ay"

样例3:

输入:s = "a"

输出:"a"


二、思路解析

1. 使用栈来模拟删除操作

我们可以使用栈来解决这个问题。栈的特性非常适合用来处理相邻重复字符的删除问题。具体步骤如下:

  • 遍历字符串中的每个字符:

    • 如果栈顶元素与当前字符相同,说明找到了一个相邻重复字符,执行删除操作(即弹出栈顶元素)。
    • 如果栈顶元素与当前字符不同,说明当前字符没有重复,直接将它压入栈中。
  • 最终栈中的元素就是处理过所有重复字符删除后的结果。

2. 时间复杂度与空间复杂度

  • 时间复杂度: O(n)O(n),其中 nn 是字符串的长度。我们只遍历一次字符串,对于每个字符,进行入栈或出栈操作,时间复杂度是常数级别的。
  • 空间复杂度: O(n)O(n),最坏情况下栈中需要存储所有字符,即当没有重复字符时,栈的空间复杂度为 O(n)O(n)。

3. 算法的核心思想

栈的先进后出(LIFO)特性使得每遇到相邻重复字符时,我们能迅速删除前一个字符,直到没有可删除的字符为止。这个过程模拟了从左到右的删除操作。


三、代码实现

#include <iostream>
#include <stack>
#include <string>

std::string removeDuplicates(std::string s) {
    std::stack<char> stk;
    
    for (char c : s) {
        if (!stk.empty() && stk.top() == c) {
            stk.pop();  // 删除栈顶的重复字符
        } else {
            stk.push(c);  // 将当前字符压入栈
        }
    }
    
    // 将栈中的字符拼接成最终的字符串
    std::string result = "";
    while (!stk.empty()) {
        result = stk.top() + result;
        stk.pop();
    }
    
    return result;
}

int main() {
    std::string s;
    std::cin >> s;
    
    std::cout << removeDuplicates(s) << std::endl;
    
    return 0;
}

四、代码解析

1. 遍历字符串:

通过遍历字符串中的每个字符,我们根据栈的当前状态来决定是否进行删除操作:

  • 栈为空或栈顶与当前字符不同:将当前字符压入栈中。
  • 栈顶元素与当前字符相同:弹出栈顶元素,表示删除相邻的相同字符。

2. 构建最终字符串:

栈中存储的是最终的字符序列。我们可以从栈顶开始逐一弹出字符,构建结果字符串。

3. 输入与输出:

程序通过 cin 接受输入字符串 s,然后通过 removeDuplicates 函数返回处理后的字符串,最后输出结果。


五、优化与总结

优化

此算法的时间复杂度已达到最优 O(n)O(n),并且空间复杂度为 O(n)O(n),其中 nn 是字符串的长度。每个字符最多入栈一次和出栈一次,因此时间和空间复杂度无法进一步优化。

栈的应用:

本题展示了栈在处理“逆向”操作时的强大能力。在处理相邻重复字符删除时,栈的先进后出特性恰好能有效模拟这一过程。

总结

这个问题通过栈的方式高效地模拟了“删除相邻相同字符”的过程,避免了暴力的字符遍历和多次删除操作。栈的使用简洁而高效,适合解决类似的字符匹配和删除问题。


六、实际应用

  • 栈的应用:

    • 栈常用于需要逆序处理或匹配成对元素的情况,典型的例子包括括号匹配、撤销操作、表达式求值等问题。
  • 字符串处理:

    • 本题解决了一个经典的字符串去重问题,实际上可以扩展为处理更复杂的字符串操作,如括号匹配、反转字符串等问题。

七、扩展思考

  • 不同字符集的扩展:

    • 本题仅处理小写字母,但如果字符集扩展为更复杂的情况(如大写字母、数字、特殊字符等),栈的思路依然适用。
  • 优化空间复杂度:

    • 可以考虑使用双指针法来优化空间复杂度,通过直接操作字符串来实现字符的删除,而不使用额外的栈空间。但栈方法更直观且易于理解。