2025-11-12:移除相邻字符。用go语言,给出一个只含小写字母的字符串 s。定义一对相邻字符为“可删”,当且仅当它们在字母表中互为相邻字母(无论顺序,如

26 阅读4分钟

2025-11-12:移除相邻字符。用go语言,给出一个只含小写字母的字符串 s。定义一对相邻字符为“可删”,当且仅当它们在字母表中互为相邻字母(无论顺序,如 ab 或 ba,都算),字母表首尾相连,因此 a 与 z 也算邻接。

操作规则:每次从左到右找出第一个可删的相邻字符对,将这两个字符删除,剩下的字符连成新的字符串。对新字符串重复上述操作,直到找不到任何可删的相邻对为止。返回最终得到的字符串。

1 <= s.length <= 100000。

s 仅由小写英文字母组成。

输入: s = "zadb"。

输出: "db"。

解释:

从字符串中移除 "za",剩下 "db"。

无法进行进一步操作。因此,所有可能移除操作后的最终字符串为 "db"。

题目来自力扣3561。

详细过程分步说明

  1. 初始化栈

    • 算法开始时,会创建一个空的栈(在Go代码中用一个字节切片st表示)。这个栈用于动态地存储那些尚未找到“可删”相邻对的字符。栈的引入使得我们可以顺序处理字符串,同时随时检查并移除新字符与栈顶字符组成的相邻对。
  2. 遍历字符串并处理每个字符

    • 算法从左到右依次处理字符串s中的每一个字符。
    • 对于当前字符,算法会检查栈是否为空。如果栈非空,则调用isConsecutive函数判断当前字符与栈顶字符是否构成“可删”对。判断标准是它们在字母表中的绝对差值为1(如ab)或25(因字母表首尾相连,az也算相邻)。
    • 如果当前字符与栈顶字符相邻:则从栈中弹出(移除)栈顶字符。这一步相当于一次性删除了这一对可删字符。弹出的字符不会被再次考虑,移除操作是即时完成的。
    • 如果当前字符与栈顶字符不相邻,或栈为空:则将当前字符压入(加入)栈顶。这个字符等待后续的字符与之配对。
    • 这个“检查-弹出/压入”的过程在单次遍历中完成,模拟了题目要求的“反复找出并删除第一个可删相邻对”的规则。栈结构巧妙地保证了每次比较和移除都发生在最新的潜在字符对之间。
  3. 构建最终结果

    • 当字符串中的所有字符都处理完毕后,栈中剩余的字符就是无法再找到可删相邻对的字符。算法将这些字符按从栈底到栈顶的顺序连接起来,形成最终的字符串并返回。

以输入s = "zadb"为例的具体过程

  • 处理z:栈为空,压入z。栈状态:[z]
  • 处理a:栈顶是zisConsecutive(z, a)计算差值为25,符合条件,因此弹出z。栈状态:[]
  • 处理d:栈为空,压入d。栈状态:[d]
  • 处理b:栈顶是disConsecutive(d, b)计算差值为2,不符合条件,因此压入b。栈状态:[d, b]
  • 遍历结束,返回栈中字符组成的字符串"db"

复杂度分析

  • 总的时间复杂度O(n),其中n是字符串s的长度。算法仅对字符串进行了一次顺序遍历,每个字符最多被压入栈一次和弹出栈一次,所有操作都是常数时间。
  • 总的额外空间复杂度O(n)。主要空间消耗在于模拟栈的切片。在最坏情况下(如没有任何字符可被移除),栈可能需要存储全部n个字符。

Go完整代码如下:

package main

import (
	"fmt"
)

func isConsecutive(x, y byte) bool {
	d := abs(int(x) - int(y))
	return d == 1 || d == 25
}

func resultingString(s string) string {
	st := []byte{}
	for _, b := range s {
		if len(st) > 0 && isConsecutive(byte(b), st[len(st)-1]) {
			st = st[:len(st)-1]
		} else {
			st = append(st, byte(b))
		}
	}
	return string(st)
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

func main() {
	s := "zadb"
	result := resultingString(s)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-

def is_consecutive(x: str, y: str) -> bool:
    d = abs(ord(x) - ord(y))
    return d == 1 or d == 25

def resulting_string(s: str) -> str:
    stack = []
    for char in s:
        if stack and is_consecutive(char, stack[-1]):
            stack.pop()
        else:
            stack.append(char)
    return ''.join(stack)

def main():
    s = "zadb"
    result = resulting_string(s)
    print(result)

if __name__ == "__main__":
    main()

在这里插入图片描述

C++完整代码如下:

#include <iostream>
#include <string>
#include <vector>
#include <cmath>

bool isConsecutive(char x, char y) {
    int d = std::abs(x - y);
    return d == 1 || d == 25;
}

std::string resultingString(const std::string& s) {
    std::vector<char> stack;
    for (char c : s) {
        if (!stack.empty() && isConsecutive(c, stack.back())) {
            stack.pop_back();
        } else {
            stack.push_back(c);
        }
    }
    return std::string(stack.begin(), stack.end());
}

int main() {
    std::string s = "zadb";
    std::string result = resultingString(s);
    std::cout << result << std::endl;
    return 0;
}

在这里插入图片描述