2026-03-01:移除K-平衡子字符串。用go语言,给定一个只含左右括号的字符串 s 和一个正整数 k。 把恰好由 k 个连续左括号紧跟 k 个连续右括号组

0 阅读7分钟

2026-03-01:移除K-平衡子字符串。用go语言,给定一个只含左右括号的字符串 s 和一个正整数 k。

把恰好由 k 个连续左括号紧跟 k 个连续右括号组成的片段称为 k-平衡串(例如 k=3 时为 "((()))")。你需要反复进行如下操作:在当前字符串中选出若干互不重叠的 k-平衡子串,一次性将它们删掉,然后把剩下的部分重新拼接成新的字符串。不断重复这一过程,直到再也找不到任何 k-平衡子串为止。

注意这里的“子串”指的是原字符串中的连续片段。因为在选择删除的过程中可能存在不同的不重叠选择,最终可能得到多种不同的结果。返回所有可能的最终字符串。

2 <= s.length <= 100000。

s 仅由 '(' 和 ')' 组成。

1 <= k <= s.length / 2。

输入: s = "((()))()()()", k = 3

输出: "()()()"

解释:

k-平衡子串是 "((()))"

步骤当前 sk-平衡结果 s
1((()))()()()((()))()()()()()()
2()()()-()()()

因此,最终字符串是 "()()()"。

题目来自力扣3703。

一、代码执行的详细分步过程

我们以输入 s = "((()))()()()"k = 3 为例,结合代码逻辑拆解每一步执行过程:

步骤1:初始化核心数据结构

代码中定义了 pair 结构体(保存「字符 + 连续出现次数」),并初始化一个栈 st(用于追踪连续括号的状态),最终结果 ans 初始化为空字节切片。

步骤2:遍历字符串的每个字符(逐个处理括号)

遍历顺序为:((()))()()(),逐个字符处理如下:

处理第1个字符 (

  • 栈为空,直接向栈中添加 pair{b: '(', cnt: 1},此时栈内容:[{ '(', 1 }]
  • 字符不是 ),无需触发「k-平衡串删除逻辑」。

处理第2个字符 (

  • 栈顶元素的字符是 (,与当前字符相同,栈顶元素的 cnt 加1 → cnt = 2,栈内容:[{ '(', 2 }]
  • 字符不是 ),无需触发删除逻辑。
处理第3个字符 (
  • 栈顶元素的字符是 (,与当前字符相同,栈顶元素的 cnt 加1 → cnt = 3,栈内容:[{ '(', 3 }]
  • 字符不是 ),无需触发删除逻辑。

处理第4个字符 )

  • 栈顶元素的字符是 (,与当前字符不同,向栈中添加 pair{b: ')', cnt: 1},此时栈内容:[{ '(', 3 }, { ')', 1 }]
  • 字符是 ),检查删除条件:
    • 栈长度 > 1(满足,长度为2);
    • 栈顶())的 cnt = 1 ≠ 3(不满足),因此不执行删除。

处理第5个字符 )

  • 栈顶元素的字符是 ),与当前字符相同,栈顶元素的 cnt 加1 → cnt = 2,栈内容:[{ '(', 3 }, { ')', 2 }]
  • 字符是 ),检查删除条件:
    • 栈顶())的 cnt = 2 ≠ 3(不满足),不执行删除。

处理第6个字符 )

  • 栈顶元素的字符是 ),与当前字符相同,栈顶元素的 cnt 加1 → cnt = 3,栈内容:[{ '(', 3 }, { ')', 3 }]
  • 字符是 ),检查删除条件:
    1. 栈长度 > 1(满足);
    2. 栈顶())的 cnt = 3(等于k,满足);
    3. 栈顶前一个元素(()的 cnt = 3(≥k,满足)。
  • 执行删除逻辑:
    • 先移除栈顶的 ) 元素 → 栈内容变为 [{ '(', 3 }]
    • 栈顶(()的 cnt 减去k → 3 - 3 = 0
    • cnt = 0,移除该 ( 元素 → 栈变为空。

处理第7个字符 (

  • 栈为空,添加 pair{b: '(', cnt: 1},栈内容:[{ '(', 1 }]
  • 字符不是 ),无需删除。

处理第8个字符 )

  • 栈顶是 (,与当前字符不同,添加 pair{b: ')', cnt: 1},栈内容:[{ '(', 1 }, { ')', 1 }]
  • 字符是 ),检查删除条件:
    • 栈顶())的 cnt = 1 ≠ 3(不满足),不删除。

处理第9个字符 (

  • 栈顶是 ),与当前字符不同,添加 pair{b: '(', cnt: 1},栈内容:[{ '(', 1 }, { ')', 1 }, { '(', 1 }]
  • 字符不是 ),无需删除。

处理第10个字符 )

  • 栈顶是 (,与当前字符不同,添加 pair{b: ')', cnt: 1},栈内容:[{ '(', 1 }, { ')', 1 }, { '(', 1 }, { ')', 1 }]
  • 字符是 ),检查删除条件:
    • 栈顶())的 cnt = 1 ≠ 3(不满足),不删除。

处理第11个字符 (

  • 栈顶是 ),与当前字符不同,添加 pair{b: '(', cnt: 1},栈内容:[{ '(', 1 }, { ')', 1 }, { '(', 1 }, { ')', 1 }, { '(', 1 }]
  • 字符不是 ),无需删除。

处理第12个字符 )

  • 栈顶是 (,与当前字符不同,添加 pair{b: ')', cnt: 1},栈内容:[{ '(', 1 }, { ')', 1 }, { '(', 1 }, { ')', 1 }, { '(', 1 }, { ')', 1 }]
  • 字符是 ),检查删除条件:
    • 栈顶())的 cnt = 1 ≠ 3(不满足),不删除。

步骤3:遍历结束后,拼接最终字符串

遍历完成后,栈中剩余的元素是: [{ '(', 1 }, { ')', 1 }, { '(', 1 }, { ')', 1 }, { '(', 1 }, { ')', 1 }]

代码遍历栈中每个 pair,将「字符重复对应次数」拼接成字符串:

  • 第1个pair:( 重复1次 → "(";
  • 第2个pair:) 重复1次 → ")";
  • 第3个pair:( 重复1次 → "(";
  • 第4个pair:) 重复1次 → ")";
  • 第5个pair:( 重复1次 → "(";
  • 第6个pair:) 重复1次 → ")";

最终拼接结果为 "()()()",与题目预期输出一致。

步骤4:关键逻辑补充(反复删除的隐含实现)

代码通过「栈实时检测并删除k-平衡串」的方式,天然实现了「反复删除直到无k-平衡串」的逻辑:

  • 每次处理字符时,只要形成k-平衡串就立即删除,删除后剩余字符的连续状态会被栈继续追踪;
  • 后续字符与删除后的剩余字符拼接后,若再次形成k-平衡串,会在遍历到对应字符时再次触发删除(本例中后续无k-平衡串,因此一次遍历即可完成)。

二、时间复杂度与空间复杂度分析

1. 时间复杂度:O(n)

  • 核心操作是「遍历字符串的每个字符」:每个字符仅入栈1次、出栈1次(若触发删除),单个字符的入栈/出栈/计数更新都是O(1)操作;
  • 最终拼接结果时,遍历栈的每个元素,栈的总元素数不超过原字符串长度n;
  • 所有操作的总次数与字符串长度n成正比,因此时间复杂度为O(n)(n为字符串s的长度)。

2. 额外空间复杂度:O(n)

  • 主要额外空间是「栈 st」:最坏情况下(无任何k-平衡串可删除),栈需要存储所有字符的 pair 结构,空间占用与n成正比;
  • 结果切片 ans 最终存储的是处理后的字符串,属于输出空间,一般不计入「额外空间复杂度」;
  • 因此额外空间复杂度为O(n)(n为字符串s的长度)。

总结

  1. 核心过程:通过栈追踪连续括号的「字符+次数」,遍历字符串时实时检测k-平衡串,一旦满足条件就删除对应片段,最终拼接栈中剩余字符得到结果;
  2. 时间复杂度:O(n)(n为字符串长度,每个字符仅被处理常数次);
  3. 空间复杂度:O(n)(栈的最大空间不超过字符串长度)。

Go完整代码如下:

package main

import (
	"fmt"
	"strings"
)

func removeSubstring(s string, k int) string {
	type pair struct {
		b   rune
		cnt int
	}
	st := []pair{} // 栈中保存 pair{字符, 连续出现次数}
	for _, b := range s {
		if len(st) > 0 && st[len(st)-1].b == b {
			st[len(st)-1].cnt++ // 连续相同括号个数 +1
		} else {
			st = append(st, pair{b, 1}) // 新的括号
		}

		// 栈顶的 k 个右括号与栈顶下面的 k 个左括号抵消
		if b == ')' && len(st) > 1 && st[len(st)-1].cnt == k && st[len(st)-2].cnt >= k {
			st = st[:len(st)-1]
			st[len(st)-1].cnt -= k
			if st[len(st)-1].cnt == 0 {
				st = st[:len(st)-1]
			}
		}
	}

	ans := []byte{}
	for _, p := range st {
		ans = append(ans, strings.Repeat(string(p.b), p.cnt)...)
	}
	return string(ans)
}

func main() {
	s := "((()))()()()"
	k := 3
	result := removeSubstring(s, k)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

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

def remove_substring(s: str, k: int) -> str:
    st = []  # 栈中保存 [字符, 连续出现次数]
    
    for b in s:
        if st and st[-1][0] == b:
            st[-1][1] += 1  # 连续相同字符个数 +1
        else:
            st.append([b, 1])  # 新的字符

        # 栈顶的 k 个右括号与栈顶下面的 k 个左括号抵消
        if b == ')' and len(st) > 1 and st[-1][1] == k and st[-2][1] >= k:
            st.pop()  # 移除栈顶的 k 个右括号
            st[-1][1] -= k  # 左括号减少 k 个
            if st[-1][1] == 0:
                st.pop()  # 如果左括号数量变为 0,也移除

    # 构建结果字符串
    result = []
    for char, count in st:
        result.append(char * count)
    
    return ''.join(result)

def main():
    s = "((()))()()()"
    k = 3
    result = remove_substring(s, k)
    print(result)

if __name__ == "__main__":
    main()

在这里插入图片描述

C++完整代码如下:

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

struct Pair {
    char b;     // 字符
    int cnt;    // 连续出现次数
};

std::string removeSubstring(std::string s, int k) {
    std::vector<Pair> st;  // 栈中保存 Pair{字符, 连续出现次数}

    for (char b : s) {
        if (!st.empty() && st.back().b == b) {
            st.back().cnt++;  // 连续相同字符个数 +1
        } else {
            st.push_back({b, 1});  // 新的字符
        }

        // 栈顶的 k 个右括号与栈顶下面的 k 个左括号抵消
        if (b == ')' && st.size() > 1 && st.back().cnt == k && st[st.size() - 2].cnt >= k) {
            st.pop_back();  // 移除栈顶的 k 个右括号
            st.back().cnt -= k;  // 左括号减少 k 个
            if (st.back().cnt == 0) {
                st.pop_back();  // 如果左括号数量变为 0,也移除
            }
        }
    }

    // 构建结果字符串
    std::string result;
    for (const auto& p : st) {
        result.append(p.cnt, p.b);  // 添加 p.cnt 个 p.b 字符
    }

    return result;
}

int main() {
    std::string s = "((()))()()()";
    int k = 3;
    std::string result = removeSubstring(s, k);
    std::cout << result << std::endl;

    return 0;
}

在这里插入图片描述