📅  最后修改于: 2023-12-03 15:28:49.180000             🧑  作者: Mango
题目链接:门|门CS 2013 |第 50 题
给你一个长为 $n$ 的01串 $s$,再给你一个数组 $a$,$a_i∈[1,n]$ 表示 $s$ 的某一个位置可以改变原来的数字,结果只能是改成 $i$。 要求你改变 $s$ 中最少的数字,使得 $s$ 中每个位置为 1 的左右两边都有一个位置为 0。 输出最小的改变次数。
第 $1$ 行,一个正整数 $n$。
第 $2$ 行,一个长为 $n$ 的01串 $s$,保证 $s$ 中至少有 $2$ 个0。
第 $3$ 行,一个正整数 $m$,表示数组 $a$ 的长度。
第 $4-3+m$ 行,每行一个正整数 $a_i$ ,保证不重复。
输出一个整数,表示最小的改变次数。
10
1110100101
2
2
7
1
将第 $2$ 个 $1$ 改为 $0$ 即可。
本题可以使用贪心算法求解:
在第 2 步中,找到的左右两边的 01 段可以使用二分查找来加速。
时间复杂度为 $O(m\log m)$,空间复杂度为 $O(m)$。
n = int(input())
s = input()
m = int(input())
a = sorted(list(map(int, input().split())))
# 构造 01 段列表
sections = []
start = 0
for i in range(n):
if s[i] == '0':
sections.append((start, i))
start = i + 1
sections.append((start, n))
# 统计未覆盖的 01 段个数
uncovered = len(sections) - 2
# 标记每个 01 段是否已被覆盖
flags = [False] * (len(sections) - 1)
# 循环直到所有的 01 段都被覆盖
ans = 0
while uncovered > 0:
# 枚举每个未覆盖的 01 段
for i in range(len(sections) - 1):
if not flags[i]:
left, right = sections[i]
# 查找距离该 01 段最近的满足要求的左侧 01 段
lidx = bisect_left(sections, (0, left))
while lidx >= 0 and flags[lidx]:
lidx -= 1
lidx += 1
# 查找距离该 01 段最近的满足要求的右侧 01 段
ridx = bisect_right(sections, (right, n))
while ridx < len(sections) - 1 and flags[ridx]:
ridx += 1
if lidx < i and ridx > i:
flags[i] = True
uncovered -= 1
continue
# 如果没有满足要求的 01 段,则需要进行修改
idx = closest_idx(left, right, a)
ans += 1
sections.insert(i + 1, (a[idx] - 1, a[idx] - 1))
flags.insert(i + 1, False)
a.pop(idx)
print(ans)