2025-09-25:操作后最大活跃区段数Ⅱ 用go语言,给出一个长度为

B站影视 日本电影 2025-09-25 07:10 1

摘要:2025-09-25:操作后最大活跃区段数Ⅱ。用go语言,给出一个长度为 n 的二进制字符串 s,其中 1 代表“活跃”区段,0 代表“非活跃”区段。你可以最多进行一次特殊的变换来尽可能增加字符串中活跃区段(即由若干相邻 1 组成的连续段)的数量。一次变换的步

2025-09-25:操作后最大活跃区段数Ⅱ。用go语言,给出一个长度为 n 的二进制字符串 s,其中 1 代表“活跃”区段,0 代表“非活跃”区段。你可以最多进行一次特殊的变换来尽可能增加字符串中活跃区段(即由若干相邻 1 组成的连续段)的数量。一次变换的步骤是:

• 先把某个两端被 0 包围的连续 1 段全部改为 0;

• 再把某个两端被 1 包围的连续 0 段全部改为 1。
此外有若干个查询 queries,每个查询给出区间 [li, ri],表示只在子串 s[li..ri] 范围内应用上述一次变换来追求活跃区段数的最多可能值。注意:为便于判定被“包围”的边界,每个单独处理的子串在两端都临时加上一个额外的 1(构成 t = '1' + s[li..ri] + '1'),但这两个额外的 1 本身不计入最终活跃区段数。每个查询互不影响。要求返回一个数组 answer,其中 answer[i] 是对应查询在做最优变换后能得到的最大活跃区段数的结果。
1 <= n == s.length <= 100000。
1 <= queries.length <= 100000。
s[i] 只有 '0' 或 '1'。
queries[i] = [li, ri]。
0 <= li <= ri < n。
输入: s = "0100", queries = [[0,3],[0,2],[1,3],[2,3]]。

输出: [4,3,1,1]。

解释:

查询 [0, 3] → 子字符串 "0100" → 变为 "101001"
选择 "0100","0100" → "0000" → "1111"。
最终字符串(去掉添加的 '1')为 "1111"。最大活跃区间数为 4。

查询 [0, 2] → 子字符串 "010" → 变为 "10101"
选择 "010","010" → "000" → "111"。
最终字符串(去掉添加的 '1')为 "1110"。最大活跃区间数为 3。

查询 [1, 3] → 子字符串 "100" → 变为 "11001"
因为没有被 '0' 包围的 '1' 区域,所以没有有效的操作可以进行。最大活跃区间数为 1。

查询 [2, 3] → 子字符串 "00" → 变为 "1001"

题目来自力扣3501。

题目允许一次特殊变换:

1. 选择一个两端被 0 包围的连续 1 段(即形如 011...110 在子串内部)全部变成 0;

2. 再选择一个两端被 1 包围的连续 0 段(即形如 100...001 在子串内部)全部变成 1。

注意:子串 s[li..ri] 在判断“包围”条件时,会在两端临时加上 1(即 t = '1' + s[li..ri] + '1'),但最终统计活跃段数时只统计原子串内的连续 1 段数(即去掉添加的边界 1)。

目标:最大化最终活跃段(连续 1 段)的数量。

1. 扫描原串 s,将连续的相同字符分段。

• 例如 s = "0100" 分段为:0, 1, 00。

• 代码中的 a 列表存储的是非活跃段(0 段) 的区间 [l, r](左闭右开?这里要看代码细节,但逻辑上是记录 0 段的起止位置)。

• a[0] 和 a[len(a)-1] 是哨兵区间({-1,-1} 和 {n+1, n+1}),方便边界处理。

• belong[i] 记录位置 i 属于哪个 0 段(如果 s[i] 是 0)或属于哪个 0 段右侧(如果 s[i] 是 1)。

2. 总活跃段长度 total1 是原串中所有 1 的数量(因为最终活跃段数 = 总 1 的段数 + 可能通过操作增加的段数)。

• 对每个 0 段,考虑它与下一个 0 段之间的 1 段(即 a[i] 与 a[i+1] 之间的 1 段)合并后的长度:st[i][0] = (a[i].r - a[i].l) + (a[i+1].r - a[i+1].l)?这里代码里是 p.r - p.l + a[i+1].r - a[i+1].l,其实是在计算相邻两个 0 段之间的 1 段总长度(因为操作可能合并两个 0 段之间的 1 段来增加活跃段数)。

• 建立 ST 表是为了快速查询任意两个 0 段之间的最大合并收益。

对于查询 [ql, qr]:

1. 确定受影响的 0 段范围

• 找到 ql 所在的 0 段索引 i,以及 qr 所在的 0 段索引 j。

• 调整 i 和 j 使得它们表示在查询范围内完整的 0 段区间 [i, j]。

2. 分类讨论

• 如果 i > j:说明查询区间内没有完整的 0 段(可能只有部分 0 段),则只能合并两端的部分段。

• 如果 i

3. 计算最大收益 mx

• 收益来源于将一段连续的 0 变成 1 后,能连接两边的 1 段,从而减少活跃段的分割,增加总活跃段数?这里要小心:实际上操作是减少一个 1 段并增加一个 1 段,但关键是选择得当可以净增加段数。

• 代码中 mx 计算的是通过操作能增加的最大连续 1 的长度(因为最终活跃段数 = 原总 1 段数 + 新增的段数,但这里似乎直接加 mx 到 total1?需要确认逻辑)。

4. 答案计算

• ans[qi] = total1 + mx,意味着 mx 是操作后相比原串增加的活跃段数(或增加的 1 的个数?这里可能是直接加长度,但题目要求段数,可能这里把“段数”转化为“总 1 的个数”来简化,因为一段连续的 1 的段数贡献为 1,但长度增加可以连接段,可能代码实际是求最大可能的一段 1 的长度,最终段数 = 该长度对应的段数?需要细看)。

• 原串分段:0, 1, 00。

• total1 = 1(只有中间一个 1 段)。

• 查询 [0,3](整个串):

加边界 1 后:1 0 1 0 0 1。选择第一个 1 段(0 之间的 1)变成 0,再选择第一个 0 段(1 之间的 0)变成 1,最终得到 1 1 1 1 去掉边界后为 1111,段数为 1,但题目输出是 4?这里可能我理解有误:实际上最终统计的是整个原串的活跃段数,而不是子串?不对,题目说只统计子串内的活跃段数。但例子输出是 4,说明是把所有位置都变成 1 了,所以段数是 1,但为什么是 4?可能题目中“活跃段数”是指1 的个数而不是连续段数?但题目定义是连续段数。这里矛盾,可能是题目描述与输出不一致,或者是把“最终字符串”理解成整个串,而例子中整个串变成了 1111,段数为 1,但输出是 4,说明他们可能把“活跃段数”定义为 1 的个数?那么代码里的 total1 就是 1 的总数,mx 是能额外增加的 1 的个数。

这样解释合理:题目实际是最大化 1 的总数,而不是连续段数。因为操作后,一段连续的 1 无论多长,段数都是 1,但例子输出是 4,说明是统计 1 的个数。那么“活跃段数”是误导,实际是“活跃单元数”。

这样代码逻辑就通了:

• total1 是原串 1 的个数。

• 操作可以增加 1 的个数,增加的最大数量 = 选择的 0 段的长度(因为 0 变 1)。

• 但要保证操作合法(有被 1 包围的 0 段和被 0 包围的 1 段)。

• ST 表用于快速找到最大的可翻转 0 段(及其相邻 1 段合并后的总收益)。

预处理

扫描原串分段:O(n)。建立 ST 表:O(n log n)。

每个查询

确定 i, j:O(1)。ST 表查询:O(1)。总查询时间:O(q)。

总时间复杂度:O(n log n + q)。

空间复杂度

ST 表:O(n log n)。分段数组等:O(n)。总空间:O(n log n)。package mAInimport ( "fmt" "math/bits")type pair struct{ l, r int }type ST intfunc newST(a pair) ST { n := len(a) - 1 sz := bits.Len(uint(n)) st := make(ST, n) for i, p := range a[:n] { st[i] = make(int, sz) st[i][0] = p.r - p.l + a[i+1].r - a[i+1].l } for j := 1; j byte(b) != s[i+1] { if s[i] == '1' { total1 += i - start + 1 } else { a = append(a, pair{start, i + 1}) } start = i + 1 } } a = append(a, pair{n + 1, n + 1}) merge := func(x, y int)int { if x > 0 && y > 0 { return x + y } return0 } st := newST(a) ans := make(int, len(queries)) for qi, q := range queries { ql, qr := q[0], q[1] i := belong[ql] if ql > 0 && s[ql] == '0' && s[ql-1] == '0' { i++ // i 在残缺区间中 } j := belong[qr] - 1 if qr+1 # -*-coding:utf-8-*-import mathfrom typing import List, Tupleclass ST: def __init__(self, a: List[Tuple[int, int]]): n = len(a) - 1 if n int: if l >= r: return0 k = (r - l).bit_length - 1 return max(self.st[l][k], self.st[r - (1 List[int]: n = len(s) total1 = 0 belong = [0] * n # 每个 0 所属的区间下标,每个 1 右边最近的 0 区间下标 a = [(-1, -1)] # 存储区间信息 start = 0 for i in range(n): belong[i] = len(a) # 记录 if i == n-1 or s[i] != s[i+1]: if s[i] == '1': total1 += i - start + 1 else: a.append((start, i + 1)) start = i + 1 a.append((n + 1, n + 1)) def merge(x: int, y: int) -> int: if x > 0 and y > 0: return x + y return0 st = ST(a) ans = [0] * len(queries) for qi, q in enumerate(queries): ql, qr = q[0], q[1] i = belong[ql] if ql > 0 and s[ql] == '0' and s[ql-1] == '0': i += 1 # i 在残缺区间中 j = belong[qr] - 1 if qr + 1

我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。

来源:科技小树林

相关推荐