
2026-04-27:使子字符串变交替的最少删除次数。用go语言,给定一个只含字符 A 和 B 的字符串 s,长度为 n。随后会有若干操作查询 queries,总数为 q。每条查询可能是两种形式之一: 1.[1, j]:把字符串中位置 j 的字符翻转(A ↔ B)。该操作会改变字符串 s,并影响后续所有查询。 2.[2, l, r]:在不修改字符串 s 的前提下,针对区间 [l, r] 的子串,求让它变为“交替字符串”所需的最少删除字符数。
请为所有类型 [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]],分步骤拆解整个逻辑,全程不写代码,只讲核心流程。
BA是交替,AAA不是;BA无相邻重复,删除数=0;AAA有2组相邻重复(位置0-1、1-2),删除数=2;n=3,创建一个长度适配的树状数组,专门存储相邻重复字符的标记;ABA,检查每一组相邻字符:A和B → 不重复,不标记;B和A → 不重复,不标记;[1,2](对应原字符串BA);0加入答案数组,此时答案数组为[0]。B,翻转后变成A);A):翻转后A和A重复 → 给树状数组标记+1;A):翻转后A和A重复 → 给树状数组标记+1;AAA,树状数组记录了2个相邻重复标记;[0,2](对应修改后的字符串AAA);2加入答案数组,最终答案数组为[0, 2]。.
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)
}

.
# -*-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()
.
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Fenwick {
private:
vector<int> 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<int> minDeletions(string s, vector<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<char> 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<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;
return0;
}
