【名词解释】
第一行输入两个整数
——字符串长度与目标子序列数量。
第二行输入一个长度为
的 01 串
(下标从
开始)。
若不存在满足要求的区间,输出单独一行-1;否则输出两个整数
表示区间端点(输出任意一组均可)。
4 2 0011
1 3
子串内的
子序列共有
个:
、
。
4 2 1110
-1
注意到字串变长时,其中的 01 子序列数量单调不减,故可以用二分。
(注释由 AI 生成,但是 AI 太笨了不会讲原理,基本只是在解释每一步做了什么)
# 导入operator模块的le方法(注:当前代码中未实际使用该方法,属于冗余导入)
from operator import le
# 读取输入的n和k:n为字符串长度,k为目标匹配值
# strip()去除首尾空白,split()按空格分割,map(int, ...)转换为整数类型
n, k = map(int, input().strip().split())
# 读取输入的二进制字符串s(仅包含'0'和'1')
s = input().strip()
# 前缀数组:pre_0[i] 表示字符串s的前i个字符(s[0..i-1])中'0'的个数
# 长度为n+1,方便处理边界情况(前缀和常用初始化方式)
pre_0 = [0] * (n + 1)
# 后缀数组:suf_1[j] 表示字符串s从j位置开始到末尾(s[j..n-1])中'1'的个数
suf_1 = [0] * n
# 后缀数组:suf_01[j] 表示字符串s从j位置开始到末尾,每个'0'对应的后续'1'的个数之和
# 即对于s[j..n-1]中的每个'0',累加其右侧(包括自身之后)的'1'数量,最终求和
suf_01 = [0] * n
# 初始化suf_1的最后一个元素(最右侧字符):如果是'1',则后缀1的个数为1
if s[-1] == '1':
suf_1[-1] = 1
# 循环遍历,同时构建前缀数组pre_0和后缀数组suf_1、suf_01
for i in range(n):
# 构建pre_0:继承前一个位置的前缀0个数(pre_0[i-1])
# 注:i=0时,pre_0[i-1] = pre_0[-1],即pre_0数组的最后一个元素(初始为0)
pre_0[i] = pre_0[i - 1]
# 如果当前字符是'0',前缀0的个数加1
if s[i] == '0':
pre_0[i] += 1
# 当i>0时,反向遍历构建后缀数组(j = n-1-i 从倒数第二个字符向前遍历)
if i > 0:
# 计算反向遍历的索引j(从后往前第i+1个位置)
j = n - 1 - i
# 构建suf_1:继承j+1位置的后缀1个数(右侧的后缀1总数)
suf_1[j] = suf_1[j + 1]
# 如果当前字符是'1',后缀1的个数加1
if s[j] == '1':
suf_1[j] += 1
# 构建suf_01:继承j+1位置的suf_01值(右侧的0对应1的累计和)
suf_01[j] = suf_01[j + 1]
# 如果当前字符是'0',则累加当前位置右侧的1的个数(suf_1[j])到suf_01[j]
if s[j] == '0':
suf_01[j] += suf_1[j]
# 自定义二分查找函数:在区间[i, n-1]中查找符合target条件的最大索引j
# 参数i:查找的左边界起始位置;target:目标匹配值
def bisect(i, target):
# 二分查找的左边界(起始为i)和右边界(末尾为n-1)
left = i
right = n - 1
# 二分查找循环:当左边界不超过右边界时继续
while left <= right:
# 计算中间索引mid(等价于(left + right) // 2,位运算更高效)
mid = left + right >> 1
# 计算决策值dec:用于判断是否满足目标条件
# 组成:suf_01[mid](mid右侧0对应1的累计和) + 区间[i, mid-1]内0的个数 * suf_1[mid](mid右侧1的个数)
dec = suf_01[mid] + (pre_0[mid - 1] - pre_0[i - 1]) * suf_1[mid]
# 如果dec大于等于目标值,说明需要向右查找更大的索引
if dec >= target:
left = mid + 1
# 否则向左查找
else:
right = mid - 1
# 循环结束后,计算右边界right对应的dec值(验证是否符合目标)
dec = suf_01[right] + (pre_0[right - 1] - pre_0[i - 1]) * suf_1[right]
# 如果dec等于target,返回right(找到符合条件的索引);否则返回特殊值-114514(标记未找到)
return right if dec == target else -114514
# 核心求解函数:查找满足条件的字符串区间[i+1, j](题目要求输出1-based索引)
def solve():
# 遍历每个可能的起始位置i(0-based,对应字符串s的第i个字符)
for i in range(n):
# 情况1:如果从i位置到末尾的suf_01值恰好等于k,直接返回区间[i+1, n](1-based)
if suf_01[i] == k:
return f'{i + 1} {n}'
# 情况2:如果从i位置到末尾的suf_01值小于k,说明不存在符合条件的区间,返回'-1'
if suf_01[i] < k:
return '-1'
# 情况3:计算目标值target = 当前suf_01[i] - k,用于二分查找
target = suf_01[i] - k
# 调用二分查找函数,查找符合target条件的j
j = bisect(i, target)
# 如果j返回特殊值(未找到),继续遍历下一个i
if j < 0:
continue
# 找到符合条件的j,返回区间[i+1, j](1-based索引)
return f'{i + 1} {j}'
# 调用求解函数并打印结果
print(solve())
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⠶⠟⠛⠛⠛⠛⠷⡶⠶⠿⠛⠷⠶⣦⣤⡀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠶⣄
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢶⡀
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⠫⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢄⠀⠀⠀⠀⠉⢷⡀
# ⠀⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠒⡀⠀⠀⠀⠙⣦
# ⠀⠀⠀⠀⣠⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢆⠀⠀⠀⠘⣧
# ⠀⠀⠀⣴⠁⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠈⢿⡀
# ⠀⠀⡾⠀⣠⢾⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠀⠀⠀⠹⣆
# ⠀⢸⢁⠞⠀⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣿⠀⠀⠀⠀⠀⠀⢰⣿⠑⠢⣄⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠘⣆
# ⠀⠘⠃⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠠⠋⢀⢏⢿⠀⠀⠀⠀⠀⢀⣿⣌⣆⠀⠀⣏⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠈⠀⠀⢄⠀⠀⢹⡀
# ⠀⠀⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⢠⠀⠀⡟⠟⢞⣇⠀⠀⠀⠀⢿⠟⠀⠙⣆⠀⢹⢦⡀⠀⠘⠀⠀⠀⠀⠀⠀⣄⠀⠀⠀⢳⡀⠀⣇
# ⠀⠀⠀⣤⠋⠀⠀⠀⠀⠀⠀⢀⡿⡄⢸⠙⠀⠀⠻⣆⢠⣄⠀⠀⣧⠀⠀⠀⠛⢦⣷⠙⢶⣄⠀⠀⠀⠀⠀⠀⠹⡀⠀⢠⣼⠻⡀⡟
# ⢀⣴⠟⠀⠀⠀⣀⠞⠀⠀⠀⣼⢋⠈⠻⠀⢀⣪⡶⠀⠛⠃⠉⠛⠻⣬⣑⣀⣀⣀⣤⣶⣷⣸⠀⠀⠀⠀⠠⠀⠀⠙⣄⠀⣿⠀⠛
# ⠀⠉⠛⠛⡿⠁⠀⠀⣄⠀⠀⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⡿⠿⡿⠀⠀⠀⠀⠀⡾⠓⠶⠶⠋⠘⣦
# ⠀⠀⢀⠟⠀⠀⠀⣼⣿⣦⡀⠹⡀⠀⠙⠁⠀⠀⠒⠶⠒⠲⠤⠴⠀⠀⠀⠀⠁⠀⠀⠀⣾⠀⠀⠀⠀⠀⡾⠉⢷⣳⢄⠀⠀⠈⠳⣄
# ⠀⠀⡿⠀⠀⠀⣠⣿⣿⢿⣿⠉⠉⠎⢀⠀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠀⠃⢸⠀⣿⡤⣶⠁⠀⢠⣿⣿⠛⢀⣿⡇⡿⠶⣤⣤⣤⠟
# ⠀⠀⡇⠀⡴⢋⣼⠗⣉⢯⣿⣷⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡾⠀⣀⣾⠉⠉⠀⣠⣿⣿⣧⣿
# ⠀⠀⠹⠟⠀⣧⡴⣻⠃⣾⣿⣿⣋⠛⢶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣛⣿⣿⣍⣴⣭⣿⡻⣿⣿⣿⣿⣞⣦
# ⠀⠀⠀⠀⠀⣾⣿⠃⢰⣿⠟⠐⣿⢏⣿⣿⣝⣿⣿⣿⠿⠶⠶⠶⠶⠶⠶⣿⠟⣩⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣷⣿⣦⣤
# ⠀⠀⠀⠀⠀⠠⣧⡀⣿⠁⢀⠋⢠⠋⠀⠙⠀⠉⠁⠀⠀⠀⠀⠀⠀⣴⣻⣿⣿⠦⣠⠛⠋⠁⣿⣿⠋⠻⣿⣿⣿⠉⠛⠿⠿⠛⠋
# ⠀⠀⠀⠀⠀⠀⠀⠀⣿⣾⢻⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢸⣿⡿⣵⠛⠀⠀⠀⠀⠁⠀⠀⠀⣿⠟
# ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣏⡟⡟
#