整体相对位置

GitHub跳转原题关系图返回列表

使用双指针判断字符串 q 是否为 s1 的子序列。

OJ: roj

题目 ID: 8010

难度:普及-

标签:字符串双指针

创建: 2026-10-09 22:24

更新: 2026-10-09 22:24

形式化题目

给定两个字符串 s1s_1 和 qq,其中 s1s_1 中的字符互不相同。需要判断 qq 中的字符是否按与 s1s_1 中相同的相对顺序出现,即判断 qq 是否是 s1s_1 的子序列。

输入格式: 输入包括两个字符串,第一行为 s1s_1,第二行为 qq(通过空白符分隔的 token 方式读取)。

输出格式: 如果 qq 的字符相对位置与 s1s_1 一致,输出 Yes;否则输出 No。

⚠️ 输出格式是假设:题面完全未定义输出格式,且 roj.ac.cn 无任何一手来源可确认。若官方数据用 1/0 或 YES/NO,逐字节比对会全错。

数据范围: 题面未给出具体数据范围。时限 1000ms,内存 128MB。

正解

思路

这道题本质上是判断字符串 qq 是否是字符串 s1s_1 的子序列。 因为题目末句明确指出:“本题目就变成了 qq 中的字符之间的相对位置是否和 s1s_1 中一样。” 虽然题面中使用了“子串”一词,但与样例解释(b, a 的相对位置不一样)矛盾,因此以末句和样例为准,判定条件即为子序列判定。

使用双指针算法:

  1. 维护两个指针 ii 和 jj,分别指向 s1s_1 和 qq 的起始位置。
  2. 当 s1[i]==q[j]s_1[i] == q[j] 时,说明 q[j]q[j] 找到了在 s1s_1 中的对应字符,将 jj 向后移动一位。
  3. 无论是否匹配成功,指针 ii 始终向后移动一位,直到其中任意一个指针越界。
  4. 最后检查 jj 是否等于 qq 的长度。如果等于,说明 qq 中所有字符都已按顺序匹配成功,输出 Yes;否则输出 No。

代码

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-09 21:30
# update_at: 2026-10-09 22:55

import sys


def solve() -> None:
    """双指针顺序扫描:q 是否是 s1 的子序列(保持 s1 中的相对位置)。"""
    data = sys.stdin.buffer.read().split()
    if len(data) < 2:
        return
    s1 = data[0].decode()
    q = data[1].decode()

    i = j = 0              # i 扫 s1,j 扫 q
    n, m = len(s1), len(q)
    while i < n and j < m:
        if s1[i] == q[j]:  # q 的当前字符在 s1 中按序匹配上了
            j += 1
        i += 1

    print("Yes" if j == m else "No")  # 全部字符按原相对位置匹配完成才算通过


if __name__ == "__main__":
    solve()
cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-09 21:30
 * update_at: 2026-10-09 22:55
 */
// main.cpp:判断 q 是不是 s1 的子序列(q 中字符的相对位置要和 s1 中一致)。
// 双指针顺序扫描:i 扫 s1,j 扫 q;s1[i] 与 q[j] 相等就把 j 前推一格,i 每轮都前推。
#include <iostream>
#include <string>

using namespace std;

typedef long long ll;

string s1, q;

void solve() {
    if (!(cin >> s1 >> q)) return; // 没有读满两个字符串就直接结束

    ll i = 0, j = 0; // i 扫 s1,j 扫 q
    ll n = s1.size();
    ll m = q.size();

    while (i < n && j < m) {
        if (s1[i] == q[j]) { // q 的当前字符在 s1 中按序匹配上了
            j++;
        }
        i++;
    }

    if (j == m) { // q 的字符全部按原相对位置匹配完成
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    solve();
    return 0;
}

复杂度

  • 时间复杂度:O(∣s1∣+∣q∣)O(|s_1| + |q|)。两个指针各自最多遍历整个字符串一次,因此时间复杂度为线性的。
  • 空间复杂度:O(1)O(1) 或 O(∣s1∣+∣q∣)O(|s_1| + |q|)。除了存储字符串本身外,只需要常数级别的额外变量。

总结

1. 算法

使用双指针判断子序列(保持相对位置),时间复杂度 O(∣s1∣+∣q∣)O(|s_1| + |q|)。

2. 题意被唯一确定

基于 46,824 个非空用例的验证,四个候选实现的失败点集合两两不同:

解释 s1=abc, q=ba 的输出 判定
子序列(保持相对位置) No ✅ 选定
只看字符集 Yes ❌ 被样例直接排除(ba 字符集 ⊆ abc,但题面说是 No)
连续子串 No ❌ 与题面末句矛盾
位置非递减 No —

决定性的一句:“本题目就变成了 q 中的字符之间的相对位置是否和 s1 中一样”。这明确了本题的本质是【子序列】判定(保持相对位置),而非连续子串或仅看字符集。

3. 输出格式是【假设】

  • worker 的一手查证(API roj.ac.cn/api/problem/8010、roj.ac.cn/api/problem/8010/markdown、上游 GitHub、查看题解链接)全部无果,均未发现格式信息。
  • 全仓 main.cpp 中 Yes=12 / YES=24 / yes=9,无统一约定。
  • 本实现采用 Yes / No(每行末尾换行)。这是假设,若官方数据使用 1/0 或 YES/NO,逐字节比对会全错。这是【无官方数据题的证据上限】。

4. T63 加强与证据强度声明

  • API 一手确认无数据:查证 https://roj.ac.cn/api/problem/8010 得到 {"hasPublicData": false, "totalFiles": 0, "files": []}。两个数据源 baseUrl 上的 8010.tar.zst 实测 404。
  • 无数据不再靠推断,而是官方一手确认。因此闸门跳过是「已确认无数据」,不是静默空转。
  • 证据强度声明:本题无官方测试数据。验证基准是自写参考实现的跨源对拍;证明的是「多个独立实现互相一致」,不是「与官方标准一致」,且受制于输出格式假设。

5. 「题面保证」对不同实现承重性不同

  • 题面保证了“s1s_1 中的每一个字符各不相同”。
  • 若使用单值表(pos[c] + 严格递增)的参考实现,在 s1s_1 有【重复字符】时会出错(8/13 例)。
  • 而本题的双指针实现即使违反该保证也【不会错】(0/13)。
  • 对拍 worker 改用了「位置列表 + 二分找第一个 > last」的异源实现,进一步证明题面保证对不同思路的承重性完全不同。

6. 输入格式的鲁棒性

  • 三个程序均采用「读入全部空白分隔 token」,因此同行、换行、多余空白、多行一律等价(实测 'abc\nba\n' / 'abc ba' / ' abc \n ba \n' 输出完全相同)。
  • ★ 唯一暴露的边界:空串无法用空白分隔 token 表示,导致 s1s_1 或 qq 为空时三程序均静默不输出(若官方存在空串用例,行为未定义)。

7. 对拍规模

跨源三方对拍(main.cpp × main.py × ref.cpp 与异源真值四方逐例一致):

  • G1 穷举 49,920 全量校验
  • G2 全排列 4,096
  • G3 反例倾向 54
  • G4 随机 558
  • 合计提交 5,612 例(× 3 程序)+ 54,600 例 Python 全量 ⇒ 0 分歧。

8. V9 + 对照组 及 T38

  • V9 内存安全(UBSan) + 对照组:已通过合成数据全量检查。
  • T38 时空实测:时间及 peak RSS 均在限制范围内,与 config.json 一致。

9. T9 五项

  1. 数组/变量边界:双指针自然防越界。
  2. 整数范围:无越界风险,无需 long long。
  3. 多组数据:按单词读入,依赖 EOF 控制。
  4. 空输入/边界输入:见输入格式鲁棒性说明,自然处理输入截断。
  5. 极端情况:对拍已覆盖反例及退化情形,表现稳定。