[R79E] 这是一道01串题5
数据规模:, 为 01 串。
思路
翻转不改变字符间的相对顺序,只改变每个字符的「当前值」。 维护一个全局翻转奇偶性 ( 表示累计翻转了偶数次)。无论怎么删,剩余串永远是原串的一个连续子串 ,位置 上的字符当前读作 。
关键不变量:每次删除后,奇偶性恰好等于被删字符的原始值。 删除位置 时其当前值为 ;若它是 则触发翻转,新奇偶性
若没触发翻转,则 ,同样有 。
可达状态的分析。 到达 的代价恒为 ,与被删顺序无关。而最后一步删除不是从左边删(位置 )就是从右边删(位置 ),由不变量,此时的奇偶性只能是 (当 )或 (当 );这两种也确实可达——先删完另一侧,最后删这一侧即可。
终止条件: 中每个字符异或 都是 ,即整段都等于 。所以剩余串必须是一个值全为 的连续段,且 是可达奇偶性。
对一个极大连续段 (长 ,值全为 ),若两端都还有字符,取整段时奇偶性被强制为 ,不合法;弃掉任一端、保留长 的一段后,最后删被弃端的外侧邻居即可让奇偶性等于 ,合法。贴边界的段同理最多保留 。唯一例外是整个串全为 :此时不动即可,答案为 。
因此答案为 ,即 减去最长连续段长度再加 ;全 串特判输出 。一次遍历同时维护当前段长与最大值即可。
复杂度
时间 ,额外空间 。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
main() {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let s = reader.readln().getOrThrow()
var maxrun: Int64 = 1
var cur: Int64 = 1
var allzero = true
if (s[0] == 49) {
allzero = false
}
var i: Int64 = 1
while (i < n) {
if (s[i] == 49) {
allzero = false
}
if (s[i] == s[i - 1]) {
cur += 1
if (cur > maxrun) {
maxrun = cur
}
} else {
cur = 1
}
i += 1
}
if (allzero) {
println(0)
} else {
println(n - maxrun + 1)
}
}