2026京东笔试真题【第几条明暗交错子序列】多语言题解

第几条明暗交错子序列(C++/Py/Java /Js/Go)题解

京东 2026年8月29号 笔试真题 第一题

题目内容

灯带车间要把一条只有暗格和亮格的样带拆成明暗交错的子序列,再按字典规矩挑出指定名次的那一条,写入当晚的工艺单。

样带是长度为 mmm 的 010101 串 b=b1b2...bmb=b_1b_2\dots b_mb=b1b2...bm,其中 0 表示暗格,1 表示亮格。值班员会选出一组下标 1≤p1<p2<⋯<pu≤m1 \le p_1 < p_2 < \dots < p_u \le m1≤p1<p2<⋯<pu≤m(uuu 可以为 0),按顺序拼成子序列 g=bp1bp2...bpug=b_{p_1}b_{p_2}\dots b_{p_u}g=bp1bp2...bpu。

如果 ggg 里没有相邻两格同暗或同亮,也就是对任意 1≤i<∣g∣1 \le i < |g|1≤i<∣g∣ 都有 gi≠gi+1g_i \neq g_{i+1}gi=gi+1,就称 ggg 是一条明暗交错带。

所有能得到明暗交错带的下标集合都算不同方案,即使拼出来的串相同也各记一次。把这些 ggg 收成一个允许重复的序列,先按长度从短到长,长度相同时再按字典序(0 小于 1)排列。空串也参与排列。

请找出排列后的第 qqq 条;如果没有第 qqq 条,输出 -1。输入保证第 qqq 条不会是空串。

询问组数与样带长度满足 1≤m≤1 \le m \le1≤m≤ 2000,2≤q≤2 \le q \le2≤q≤ 1000000000000000

输入描述

第一行两个整数 mmm、qqq(1≤m≤1 \le m \le1≤m≤ 2000,2≤q≤2 \le q \le2≤q≤ 1000000000000000),表示样带长度和要取的名次。

第二行一个长度为 mmm 的 010101 字符串 bbb。

输出描述

输出一行,即排列后的第 qqq 条明暗交错带;若不存在,输出 -1

样例1

输入

复制代码
3 4
110

输出

复制代码
1

说明

合法方案对应的串为:空串、两处单独的 1、一处 0、以及两条 10。按长度再字典序后是空串、0111010。第 4 条是 1

样例2

输入

复制代码
4 2
0101

输出

复制代码
0

说明

空串最短,下一名是长度为 1 且字典序更小的 0。q=2q=2q=2,因此输出 0

样例3

输入

复制代码
1 5
0

输出

复制代码
-1

说明

只有空串和 0 两条,没有第 5 条。

题解和思路

思路

实现思路:动态规划

  1. 虽然下标方案很多,但对于固定长度,合法的 01 交错串只有两种,(0开头或者1开头)。因此不需要枚举字符串,只需要统计每一种交错字符串作为子序列出现了多少次
  2. 定义dp数组,其中dp[len][x] x ∈[0,1],表示当前已经扫描的字符串中,长度为 len、最后一个字符是 0 的交错子序列数量。从前往后进行扫描时,扫描到第i个字符时对应要转移的状态为:
    • dp[1][x] += 1对应字符长度为1数量 + 1
    • dp[y][x] += dp[len - 1][x ^ 1], 其中1 < y <= m
  3. 根据2求出每个状态数量时,减去空串q = q - 1, 按照长度从小到大,长度相同按照0开头 到 1开头确定所处目标字符串即可。如果各种状态数量总和小于q则输出-1.
  4. 算法整体时间复杂度为O(m ^ 2)

C++

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int m;
    ll q;
    cin >> m >> q;

    string b;
    cin >> b;

    // dp[len][c]:
    // 当前扫描到的位置为止,长度为 len、最后一个字符为 c,的交错子序列数量
    vector<vector<ll>> dp(m + 1, vector<ll>(2, 0));

    // 超过 q 后就没有继续精确计算的必要
    auto addCap = [&](ll &a, ll b) {
        if (a >= q) return;
        if (b >= q - a) a = q;
        else a += b;
    };

    for (char ch : b) {
        int c = ch - '0';

        // 必须倒序,避免当前字符被重复使用
        for (int len = m; len >= 2; --len) {
            addCap(dp[len][c], dp[len - 1][1 - c]);
        }

        // 单独当前字符构成长度 1 的交错子序列
        addCap(dp[1][c], 1);
    }
    // 去掉空串
    q = q - 1;

    // 按长度从小到大寻找第 q 个
    for (int len = 1; len <= m; ++len) {
        ll count0, count1;

        if (len & 1) {
            // 奇数长度:首尾相同
            count0 = dp[len][0]; // 0101...
            count1 = dp[len][1]; // 1010...
        } else {
            // 偶数长度:首尾不同
            count0 = dp[len][1]; // 0101...
            count1 = dp[len][0]; // 1010...
        }

        // 字典序:0 开头的更小
        if (q <= count0) {
            string ans;
            ans.reserve(len);

            for (int i = 0; i < len; ++i) {
                ans += char('0' + (i & 1));
            }

            cout << ans << '\n';
            return 0;
        }

        q -= count0;

        if (q <= count1) {
            string ans;
            ans.reserve(len);

            for (int i = 0; i < len; ++i) {
                ans += char('1' - (i & 1));
            }

            cout << ans << '\n';
            return 0;
        }

        q -= count1;
    }

    cout << -1 << '\n';
    return 0;
}

Java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine());
        int m = Integer.parseInt(st.nextToken());
        long q = Long.parseLong(st.nextToken());

        String b = br.readLine();

        // dp[len][c]:
        // 当前扫描到的位置为止,长度为 len、最后一个字符为 c,的交错子序列数量
        long[][] dp = new long[m + 1][2];

        // 超过 q 后就没有继续精确计算的必要
        for (int i = 0; i < m; i++) {
            int c = b.charAt(i) - '0';

            // 必须倒序,避免当前字符被重复使用
            for (int len = m; len >= 2; len--) {
                if (dp[len][c] >= q) continue;

                long add = dp[len - 1][1 - c];

                if (add >= q - dp[len][c]) {
                    dp[len][c] = q;
                } else {
                    dp[len][c] += add;
                }
            }

            // 单独当前字符构成长度 1 的交错子序列
            if (dp[1][c] < q) {
                if (1 >= q - dp[1][c]) {
                    dp[1][c] = q;
                } else {
                    dp[1][c]++;
                }
            }
        }

        // dp 中没有统计空串,因此这里 q = q - 1
        q -= 1;
        // 按长度从小到大寻找第 q 个
        for (int len = 1; len <= m; len++) {
            long count0, count1;

            if ((len & 1) == 1) {
                // 奇数长度:首尾相同
                count0 = dp[len][0]; // 0101...
                count1 = dp[len][1]; // 1010...
            } else {
                // 偶数长度:首尾不同
                count0 = dp[len][1]; // 0101...
                count1 = dp[len][0]; // 1010...
            }

            // 字典序:0 开头的更小
            if (q <= count0) {
                StringBuilder ans = new StringBuilder();

                for (int i = 0; i < len; i++) {
                    ans.append((char) ('0' + (i & 1)));
                }

                System.out.println(ans);
                return;
            }

            q -= count0;

            if (q <= count1) {
                StringBuilder ans = new StringBuilder();

                for (int i = 0; i < len; i++) {
                    ans.append((char) ('1' - (i & 1)));
                }

                System.out.println(ans);
                return;
            }

            q -= count1;
        }

        System.out.println(-1);
    }
}

python

python 复制代码
import sys

input = sys.stdin.readline

m, q = map(int, input().split())
b = input().strip()

# dp[len][c]:
# 当前扫描到的位置为止,长度为 len、最后一个字符为 c,的交错子序列数量
dp = [[0, 0] for _ in range(m + 1)]

# 超过 q 后就没有继续精确计算的必要
def add_cap(a, b):
    if a >= q:
        return q
    if b >= q - a:
        return q
    return a + b

for ch in b:
    c = int(ch)

    # 必须倒序,避免当前字符被重复使用
    for length in range(m, 1, -1):
        dp[length][c] = add_cap(dp[length][c], dp[length - 1][1 - c])

    # 单独当前字符构成长度 1 的交错子序列
    dp[1][c] = add_cap(dp[1][c], 1)

# dp 中没有统计空串,因此需要
q = q - 1
    
# 按长度从小到大寻找第 q 个
for length in range(1, m + 1):
    if length & 1:
        # 奇数长度:首尾相同
        count0 = dp[length][0]  # 0101...
        count1 = dp[length][1]  # 1010...
    else:
        # 偶数长度:首尾不同
        count0 = dp[length][1]  # 0101...
        count1 = dp[length][0]  # 1010...

    # 字典序:0 开头的更小
    if q <= count0:
        ans = ''.join(str(i & 1) for i in range(length))
        print(ans)
        sys.exit()

    q -= count0

    if q <= count1:
        ans = ''.join(str(1 - (i & 1)) for i in range(length))
        print(ans)
        sys.exit()

    q -= count1

print(-1)

Javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const lines = [];

rl.on('line', line => {
    lines.push(line.trim());
});

rl.on('close', () => {
    const [mStr, qStr] = lines[0].split(/\s+/);

    const m = Number(mStr);
    let q = BigInt(qStr);

    const b = lines[1];

    // dp[len][c]:
    // 当前扫描到的位置为止,长度为 len、最后一个字符为 c,的交错子序列数量
    const dp = Array.from({ length: m + 1 }, () => [0n, 0n]);

    // 超过 q 后就没有继续精确计算的必要
    function addCap(a, b) {
        if (a >= q) return q;

        if (b >= q - a) {
            return q;
        }

        return a + b;
    }

    for (const ch of b) {
        const c = Number(ch);

        // 必须倒序,避免当前字符被重复使用
        for (let len = m; len >= 2; len--) {
            dp[len][c] = addCap(
                dp[len][c],
                dp[len - 1][1 - c]
            );
        }

        // 单独当前字符构成长度 1 的交错子序列
        dp[1][c] = addCap(dp[1][c], 1n);
    }

    // dp 中没有统计空串,因此需要 
     q -=  1n;
    // 按长度从小到大寻找第 q 个
    for (let len = 1; len <= m; len++) {
        let count0, count1;

        if (len & 1) {
            // 奇数长度:首尾相同
            count0 = dp[len][0]; // 0101...
            count1 = dp[len][1]; // 1010...
        } else {
            // 偶数长度:首尾不同
            count0 = dp[len][1]; // 0101...
            count1 = dp[len][0]; // 1010...
        }

        // 字典序:0 开头的更小
        if (q <= count0) {
            let ans = '';

            for (let i = 0; i < len; i++) {
                ans += String(i & 1);
            }

            console.log(ans);
            return;
        }

        q -= count0;

        if (q <= count1) {
            let ans = '';

            for (let i = 0; i < len; i++) {
                ans += String(1 - (i & 1));
            }

            console.log(ans);
            return;
        }

        q -= count1;
    }

    console.log(-1);
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var m int
	var q int64
	fmt.Fscan(in, &m, &q)

	var b string
	fmt.Fscan(in, &b)

	// dp[len][c]:
	// 当前扫描到的位置为止,长度为 len、最后一个字符为 c,的交错子序列数量
	dp := make([][2]int64, m+1)

	// 超过 q 后就没有继续精确计算的必要
	addCap := func(a *int64, b int64) {
		if *a >= q {
			return
		}

		if b >= q-*a {
			*a = q
		} else {
			*a += b
		}
	}

	for _, ch := range b {
		c := int(ch - '0')

		// 必须倒序,避免当前字符被重复使用
		for length := m; length >= 2; length-- {
			addCap(&dp[length][c], dp[length-1][1-c])
		}

		// 单独当前字符构成长度 1 的交错子序列
		addCap(&dp[1][c], 1)
	}

	// dp 中没有统计空串,因此需要 
    q = q - 1
	// 按长度从小到大寻找第 q 个
	for length := 1; length <= m; length++ {
		var count0, count1 int64

		if length&1 == 1 {
			// 奇数长度:首尾相同
			count0 = dp[length][0] // 0101...
			count1 = dp[length][1] // 1010...
		} else {
			// 偶数长度:首尾不同
			count0 = dp[length][1] // 0101...
			count1 = dp[length][0] // 1010...
		}

		// 字典序:0 开头的更小
		if q <= count0 {
			ans := make([]byte, length)

			for i := 0; i < length; i++ {
				ans[i] = byte('0' + byte(i&1))
			}

			fmt.Fprintln(out, string(ans))
			return
		}

		q -= count0

		if q <= count1 {
			ans := make([]byte, length)

			for i := 0; i < length; i++ {
				ans[i] = byte('1' - byte(i&1))
			}

			fmt.Fprintln(out, string(ans))
			return
		}

		q -= count1
	}

	fmt.Fprintln(out, -1)
}
相关推荐
xiaofeicaiyan3 天前
从财报看外卖:美团守壁垒、阿里养生态、京东换流量
美团·阿里·京东
无限码力19 天前
京东笔试真题 - 以弱胜强的秘密(C++/Py/Java /Js/Go)
京东·京东笔试真题·京东机试·京东最新笔试真题·京东技术岗笔试·京东最新笔试真题题解
无限码力19 天前
京东笔试真题 - 失落的宝石(C++/Py/Java /Js/Go)
京东·京东笔试真题·京东技术岗笔试真题·京东笔试·京东机试
电商API_1800790524721 天前
京东商品详情API技术解析:数据结构、调用实践与系统集成方案
python·api·跨境电商·京东·商品详情
加油20196 个月前
京东人事管理8项规定
管理·京东·8项规定
Cyril_KI2 年前
京东 2025届秋招 自然语言处理
自然语言处理·京东·面试经验·2025届校招
亿牛云爬虫专家2 年前
CSV文件自动化生成:用Pandas与Datetime高效处理商品信息
运维·pandas·爬虫代理·电商·代理ip·datetime·京东
27669582922 年前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
junjie xu2 年前
内推|京东|后端开发|运维|算法...|北京 更多岗位扫内推码了解,直接投递,跟踪进度
京东云·内推·京东