首页 > 试题广场 >

小红的01子序列构造(easy)

[编程题]小红的01子序列构造(easy)
  • 热度指数:6179 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}给定一个仅由字符 \tt 0,1 组成的字符串 s,长度为 n。小红想找到一个闭区间 [l,r] 使得在子串 s_{l..r} 中,恰好存在 k 个严格等于 01子序列(即选取下标 i<j,满足 s_i=\texttt{0},\ s_j=\texttt{1})。
\hspace{15pt}请你输出任意一个满足条件的区间;若不存在,则输出 -1

【名词解释】
\hspace{15pt}子序列:从字符串中删除任意个(可为零)字符后得到的字符串,保留剩余字符原有相对顺序。

输入描述:
\hspace{15pt}第一行输入两个整数 n,k\left(1\leqq n\leqq 2\times10^{5};\ 1\leqq k\leqq 10^{10}\right)——字符串长度与目标子序列数量。 
\hspace{15pt}第二行输入一个长度为 n 的 01 串 s(下标从 1 开始)。


输出描述:
\hspace{15pt}若不存在满足要求的区间,输出单独一行-1;否则输出两个整数 l,r\left(1\leqq l\leqq r\leqq n\right) 表示区间端点(输出任意一组均可)。
示例1

输入

4 2
0011

输出

1 3

说明

子串 s_{1..3}=\texttt{ 内的 01 子序列共有 2 个:s_1s_3s_2s_3
示例2

输入

4 2
1110

输出

-1
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        try {
            long n = in.nextLong(); // 改用 long
            long k = in.nextLong();
            in.nextLine();
            String s = in.nextLine();

            long left = 0;
            long num0 = 0; // 当前窗口内的 '0' 的数量
            long num1 = 0; // 当前窗口内的 '1' 的数量
            long sum = 0;  // 当前窗口内的 "01" 子序列数量

            for (long right = 0; right < n; right++) {
                if (s.charAt((int) right) == '0') {
                    num0++;
                } else {
                    sum += num0; // 当前 '1' 可以与前面所有 '0' 配对
                    num1++;
                }

                // 收缩窗口,使 sum <= k
                while (sum > k) {
                    if (s.charAt((int) left) == '0') {
                        num0--;
                        sum -= num1; // 移除 '0' 会减少它后面 '1' 的配对数量
                    } else {
                        num1--; // 移除 '1' 不影响 sum
                    }
                    left++;
                }

                if (sum == k) {
                    System.out.println((left + 1) + " " + (right + 1)); // 1-based 输出
                    return;
                }
            }
            System.out.println("-1");
        } catch (InputMismatchException e) {
            System.out.println("输入不是有效的整数!");
        }
    }
}
发表于 2025-09-10 19:46:08 回复(0)
接收控制台传过来的字符串长度和目标子序列数量,注意子序列数量可能超int值,所以需用long来接收。
保存控制台过来的字符串,用大小为2的数组分别存储0、1的数量。
定义左右指针left和right开始滑动窗口寻找符合题意的区间,
我们只需要找一个区间,当找到符合条件的区间时,用resultleft、resultright保存区间左右边界,直接返回即可。
用right滑动窗口,只有在右边界字符为1时,才有可能得到结果,当遍历到1时,res += (left, right)区间中0的个数,
当res大于目标值时,需要持续滑动窗口left,left == ‘0’时,res -= 1的数量,直到res <= target,退出循环。
此时判断res 是否等于target,若等于,说明找到了满足题意的区间,记录区间左右边界并返回、输出。
若遍历完集合还未找到,返回-1。
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNext()) { // 注意 while 处理多个 case
            int n = in.nextInt();
            Long k = in.nextLong();
            char[] chars = in.next().toCharArray();
            int[] zeroAndOne = new int[2];
            int left = 0, right = 0;
            long res = 0;
            int resultleft = 0, resultright = 0;
            for (int i = 0; i < chars.length; i++) {
                if (chars[i] == '1') {
                    res += zeroAndOne[0];
                    zeroAndOne[1]++;
                    while (res > k) {
                        if (chars[left] == '0') {
                            res -= zeroAndOne[1];
                            zeroAndOne[0]--;
                        } else if (chars[left] == '1') {
                            zeroAndOne[1]--;
                        }
                        left++;
                    }
                    if (res == k) {
                        resultleft = left + 1;
                        resultright = i + 1;
                        break;
                    }
                } else {
                    zeroAndOne[0]++;
                }
            }
            if (resultleft != 0 && resultright != 0) {
                System.out.println(resultleft + " " + resultright);
            } else System.out.println(-1);
        }
    }
}

发表于 2025-07-22 17:12:47 回复(0)
//牛蛙
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        Long k = in.nextLong();
        in.nextLine();
        String a = in.nextLine();
        //有俩种方法 第一种是先构建窗口 如果窗口内满足就输出 否则就右移
        //第二种是直接统计每个1的位置贡献了多少个0  计算哪个区间的1能够达到要求
        //使用第二种方法
        int l = 0,r=0,num0=0,num1=0;
        long num=0;
        boolean NO = true;
        for(int i = 0;i<n;i++){

            if (num == k) {
                System.out.println((l+1) + " " + (r+1));
                return;
            }
            if(num<k){
                r = i;
                if(a.charAt(r)=='1'){
                    num1++;
                    num = num + num0;
                }else{
                    num0++;
                }
            }
            if(num>k){//移多了 需要左区间右移l++
                while(num>k){
                    if(a.charAt(l)=='1'){
                        num1--;
                    }else{
                        num0--;
                        num = num-num1;
                    }
                    l++;
                }

            }

        }
        if(NO){
            System.out.println(-1);
        }

    }
}

发表于 2025-03-28 21:53:42 回复(0)