2026-04-27:使子字符串变交替的最少删除次数。用go语言,给定一个只含字符 A 和 B 的字符串 s,长度为 n。随后会有若干操作查询 queries

网易专栏3小时前发布 nxnqh
1 0 0

🤖 AI总结

主题

使用树状数组解决字符串变交替的最少删除次数问题

摘要

通过树状数组快速计算子串相邻重复字符数,实现高效查询与更新,解决力扣3777题

关键信息

  • 1 最少删除次数等于子串中相邻重复字符总数
  • 2 树状数组支持O(logn)的更新和区间查询
  • 3 示例输入输出展示了完整流程

2026-04-27:使子字符串变交替的最少删除次数。用go语言,给定一个只含字符 A 和 B 的字符串 s,长度为 n。随后会有若干操作查询 queries,总数为 q。每条查询可能是两种形式之一:
1.[1, j]:把字符串中位置 j 的字符翻转(A ↔ B)。该操作会改变字符串 s,并影响后续所有查询。
2.[2, l, r]:在不修改字符串 s 的前提下,针对区间 [l, r] 的子串,求让它变为“交替字符串”所需的最少删除字符数。

• “交替字符串”指子串中任意相邻两个字符都不相同。长度为 1 的子串天然满足该条件。

  • • 区间查询不会改变整体字符串长度 n。

    请为所有类型 [2, l, r] 的查询依次计算结果,并把这些结果按顺序组成数组 answer 返回,其中 answer[i] 对应第 i 个区间查询的最小删除数。

    1 <= n == s.length <= 100000。

    s[i] 要么是 ‘A’,要么是 ‘B’。

    1 <= q == queries.length <= 100000。

    queries[i].length == 2 或 3。

    queries[i] == [1, j] 或

    queries[i] == [2, l, r]。

    0 <= j <= n – 1。

    0 <= l <= r <= n – 1。

    输入:s = “ABA”, queries = [[2,1,2],[1,1],[2,0,2]]。

    输出:[0,2]。

    解释:

    i

    queries[i]

    j

    l

    r

    查询前的 s

    s[l..r]

    结果

    答案

    0

    [2, 1, 2]

    1

    2

    “ABA”

    “BA”

    已经是交替字符串

    0

    1

    [1, 1]

    1

    “ABA”

    将 s[1] 从 ‘B’ 反转为 ‘A’

    2

    [2, 0, 2]

    0

    2

    “AAA”

    “AAA”

    删除任意两个 ‘A’ 以得到 “A”

    2

    因此,答案是 [0, 2]。

    题目来自力扣3777。

    代码执行过程详细分步解析

    我们结合输入示例s = "ABA"(长度n=3),queries = [[2,1,2],[1,1],[2,0,2]],分步骤拆解整个逻辑,全程不写代码,只讲核心流程。

    前置知识铺垫

    1.交替字符串:相邻字符不能相同,比如BA是交替,AAA不是;

  • 2.核心规律:一个子串要变成交替字符串,最少删除次数 = 子串中相邻重复字符的总个数
    例:BA无相邻重复,删除数=0;AAA有2组相邻重复(位置0-1、1-2),删除数=2;

  • 3.树状数组(Fenwick Tree):专门用来快速记录、更新、查询区间内相邻重复字符的总数,支持O(logn)的单点修改和区间查询。

    第一步:初始化数据结构

    1. 确定字符串长度n=3,创建一个长度适配的树状数组,专门存储相邻重复字符的标记

  • 2. 遍历原字符串ABA,检查每一组相邻字符

    • 位置0和1:AB→ 不重复,不标记;

  • • 位置1和2:BA→ 不重复,不标记;

    3. 最终树状数组中没有任何标记值,代表原字符串没有相邻重复的字符。

    第二步:处理第一条查询 [2, 1, 2](类型2:区间查询)

    1. 解析查询:查询子串区间[1,2](对应原字符串BA);

  • 2. 核心逻辑:调用树状数组查询这个区间内相邻重复字符的总数

  • 3. 结果:区间内没有相邻重复字符,总数=0;

  • 4. 输出:将0加入答案数组,此时答案数组为[0]

    第三步:处理第二条查询 [1, 1](类型1:字符翻转)

    1. 解析查询:翻转字符串位置1的字符(原字符是B,翻转后变成A);

  • 2. 关键操作:翻转字符会影响它左边和右边的相邻关系,需要同步更新树状数组:

    • 位置1的左侧是位置0(原字符A):翻转后AA重复 → 给树状数组标记+1;

  • • 位置1的右侧是位置2(原字符A):翻转后AA重复 → 给树状数组标记+1;

    3. 最终字符串变为AAA,树状数组记录了2个相邻重复标记

    4. 无输出结果,仅修改字符串和树状数组。

    第四步:处理第三条查询 [2, 0, 2](类型2:区间查询)

    1. 解析查询:查询子串区间[0,2](对应修改后的字符串AAA);

  • 2. 核心逻辑:调用树状数组查询这个区间内相邻重复字符的总数

  • 3. 结果:区间内有2组相邻重复字符,总数=2;

  • 4. 输出:将2加入答案数组,最终答案数组为[0, 2]

    完整流程总结

    1. 初始化:用树状数组记录原字符串所有相邻重复字符的位置;

  • 2. 遍历所有查询:

    • 遇到类型2查询:直接用树状数组查询目标区间的相邻重复总数,就是最少删除数,加入答案;

  • • 遇到类型1查询:翻转指定位置的字符,同时更新该字符左右两侧的相邻关系(修改树状数组的标记);

    3. 所有查询处理完毕,返回答案数组。

    时间复杂度与空间复杂度分析 1. 总时间复杂度

    • 树状数组的单点更新区间查询操作,时间复杂度都是O(logn)

  • • 初始化遍历字符串:O(n);

  • • 总共有 q 条查询,每条查询最多执行2次树状数组操作(类型1)或1次(类型2);

  • • 总时间复杂度:O(n + q·logn)
    (完全适配题目中n和q最大1e5的要求,高效无超时)

    2. 总额外空间复杂度

    • 树状数组占用空间:O(n);

  • • 存储原字符串的字节数组:O(n);

  • • 答案数组:最多O(q);

  • • 总额外空间复杂度:O(n + q)
    (属于线性空间,符合大数据量的内存要求)

    总结

    1. 核心思路:最少删除次数 = 子串内相邻重复字符的个数

  • 2. 核心工具:树状数组实现快速修改+快速查询,适配1e5级别的大数据量;

  • 3. 时间复杂度:O(n + q·logn),空间复杂度:O(n + q)。

    Go完整代码如下:

    package main

    import (
    "fmt"
    )

    type fenwick []int

    func newFenwickTree(n int) fenwick {
    returnmake(fenwick, n+1) // 使用下标 1 到 n
    }

    // a[i] 增加 val
    // 1 <= i <= n
    // 时间复杂度 O(log n)
    func (f fenwick) update(i int, val int) {
    for ; i < len(f); i += i & -i {
    f[i] += val
    }
    }

    // 求前缀和 a[1] + ... + a[i]
    // 1 <= i <= n
    // 时间复杂度 O(log n)
    func (f fenwick) pre(i int) (res int) {
    for ; i > 0; i &= i - 1 {
    res += f[i]
    }
    return
    }

    // 求区间和 a[l] + ... + a[r]
    // 1 <= l <= r <= n
    // 时间复杂度 O(log n)
    func (f fenwick) query(l, r int) int {
    if r < l {
    return0
    }
    return f.pre(r) - f.pre(l-1)
    }

    func minDeletions(s string, queries [][]int) (ans []int) {
    n := len(s)
    t := newFenwickTree(n - 1)
    for i := 1; i < n; i++ {
    if s[i-1] == s[i] { // 删除 i
    t.update(i, 1)
    }
    }

    bs := []byte(s)
    for _, q := range queries {
    if q[0] == 2 {
    ans = append(ans, t.query(q[1]+1, q[2]))
    continue
    }

    i := q[1]

    if i > 0 {
    val := 1
    if bs[i-1] == bs[i] {
    val = -1
    }
    t.update(i, val)
    }

    if i < n-1 {
    val := 1
    if bs[i] == bs[i+1] {
    val = -1
    }
    t.update(i+1, val)
    }

    bs[i] ^= 'A' ^ 'B'// A 变成 B,B 变成 A
    }
    return
    }

    func main() {
    s := "ABA"
    queries := [][]int{{2, 1, 2}, {1, 1}, {2, 0, 2}}
    result := minDeletions(s, queries)
    fmt.Println(result)
    }

    2026-04-27:使子字符串变交替的最少删除次数。用go语言,给定一个只含字符 A 和 B 的字符串 s,长度为 n。随后会有若干操作查询 queries

    Python完整代码如下:

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

    class Fenwick:
    def __init__(self, n: int):
    self.n = n
    self.bit = [0] * (n + 1) # 使用下标 1 到 n

    # a[i] 增加 val
    # 1 <= i <= n
    # 时间复杂度 O(log n)
    def update(self, i: int, val: int) -> None:
    while i < len(self.bit):
    self.bit[i] += val
    i += i & -i

    # 求前缀和 a[1] + ... + a[i]
    # 1 <= i <= n
    # 时间复杂度 O(log n)
    def pre(self, i: int) -> int:
    res = 0
    while i > 0:
    res += self.bit[i]
    i &= i - 1
    return res

    # 求区间和 a[l] + ... + a[r]
    # 1 <= l <= r <= n
    # 时间复杂度 O(log n)
    def query(self, l: int, r: int) -> int:
    if r < l:
    return0
    return self.pre(r) - self.pre(l - 1)

    def min_deletions(s: str, queries: list) -> list:
    n = len(s)
    tree = Fenwick(n - 1)

    # 初始化树状数组,标记需要删除的位置
    for i in range(1, n):
    if s[i - 1] == s[i]: # 删除 i
    tree.update(i, 1)

    bs = list(s)
    ans = []

    for q in queries:
    if q[0] == 2:
    # 区间查询
    ans.append(tree.query(q[1] + 1, q[2]))
    continue

    # 更新操作 q[0] == 1
    i = q[1]

    if i > 0:
    val = 1
    if bs[i - 1] == bs[i]:
    val = -1
    tree.update(i, val)

    if i < n - 1:
    val = 1
    if bs[i] == bs[i + 1]:
    val = -1
    tree.update(i + 1, val)

    # 翻转字符 A <-> B
    bs[i] = chr(ord('A') + ord('B') - ord(bs[i]))

    return ans

    def main():
    s = "ABA"
    queries = [[2, 1, 2], [1, 1], [2, 0, 2]]
    result = min_deletions(s, queries)
    print(result)

    if __name__ == "__main__":
    main()

    2026-04-27:使子字符串变交替的最少删除次数。用go语言,给定一个只含字符 A 和 B 的字符串 s,长度为 n。随后会有若干操作查询 queries

    C++完整代码如下:

      
    


    using namespace std;

    class Fenwick {
    private:
    vector bit;
    int n;

    public:
    Fenwick(int n) : n(n) {
    bit.resize(n + 1, 0); // 使用下标 1 到 n
    }

    // a[i] 增加 val
    // 1 <= i <= n
    // 时间复杂度 O(log n)
    void update(int i, int val) {
    for (; i < bit.size(); i += i & -i) {
    bit[i] += val;
    }
    }

    // 求前缀和 a[1] + ... + a[i]
    // 1 <= i <= n
    // 时间复杂度 O(log n)
    int pre(int i) {
    int res = 0;
    for (; i > 0; i &= i - 1) {
    res += bit[i];
    }
    return res;
    }

    // 求区间和 a[l] + ... + a[r]
    // 1 <= l <= r <= n
    // 时间复杂度 O(log n)
    int query(int l, int r) {
    if (r < l) {
    return0;
    }
    return pre(r) - pre(l - 1);
    }
    };

    vector minDeletions(string s, vector int >>& queries) {
    int n = s.size();
    Fenwick tree(n - 1 );

    // 初始化树状数组,标记需要删除的位置
    for ( int i = 1 ; i < n; i++) {
    if (s[i - 1 ] == s[i]) { // 删除 i
    tree.update(i, 1 );
    }
    }

    vector bs(s.begin(), s.end());
    vector< int > ans;

    for (auto& q : queries) {
    if (q[ 0 ] == 2 ) {
    // 区间查询
    ans.push_back(tree.query(q[ 1 ] + 1 , q[ 2 ]));
    continue ;
    }

    // 更新操作 q[0] == 1
    int i = q[ 1 ];

    if (i > 0 ) {
    int val = 1 ;
    if (bs[i - 1 ] == bs[i]) {
    val = -1 ;
    }
    tree.update(i, val);
    }

    if (i < n - 1 ) {
    int val = 1 ;
    if (bs[i] == bs[i + 1 ]) {
    val = -1 ;
    }
    tree.update(i + 1 , val);
    }

    // 翻转字符 A <-> B
    // 'A' ^ 'B' 在 C++ 中是 1,因为 'A' 是 65,'B' 是 66,异或结果为 1
    // 所以 bs[i] ^= 1 可以实现 A(65) 变成 B(66),B(66) 变成 A(65)
    bs[i] ^= 1 ;
    }

    return ans;
    }

    int main() {
    string s = "ABA" ;
    vector int >> queries = {{ 2 , 1 , 2 }, { 1 , 1 }, { 2 , 0 , 2 }};
    vector< int > result = minDeletions(s, queries);

    cout << "[" ;
    for (size_t i = 0 ; i < result.size(); i++) {
    if (i > 0 ) cout << " " ;
    cout << result[i];
    }
    cout << "]" << endl;

    return 0 ;
    }

    2026-04-27:使子字符串变交替的最少删除次数。用go语言,给定一个只含字符 A 和 B 的字符串 s,长度为 n。随后会有若干操作查询 queries

    我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。

    © 版权声明

    相关文章