1 条题解

  • 0
    @ 2026-5-5 13:19:31

    CF1989B Substring and Subsequence 题解

    题意简述

    给定两个字符串 aabb(长度均 100\le 100),求最短的字符串 ss,使得:

    • aass子串(连续出现);
    • bbss子序列(不要求连续,但顺序不变)。

    输出 ss 的最小长度。


    思路分析

    n=an = |a|m=bm = |b|

    最终构造的 ss 必然包含 aa 作为连续段。同时,bb 的字符按顺序出现在 ss 中。我们可以将 bb 的字符分为三部分:

    1. aa之前出现的部分(bb 的某个前缀);
    2. aa内部作为子序列匹配的部分(bb 的一段连续子串);
    3. aa之后出现的部分(bb 的某个后缀)。

    要使 ss 最短,我们应尽可能多地利用 aa 内部的字符去覆盖 bb 的字符,从而减少需要额外放在 aa 前后的字符数量。

    具体地,假设我们能从 aa 中找到 bb 的一个连续子段 b[i..r1]b[i..r-1] 作为子序列(即存在 aa 的一些位置,顺序等于 b[i..r1]b[i..r-1]),那么可以这样构造 ss

    s=b[1..i1]  +  a  +  b[r..m]s = b[1..i-1] \;+\; a \;+\; b[r..m]

    这样 ss 的长度为:

    s=(i1)+n+(mr+1)=n+m(ri)|s| = (i-1) + n + (m - r + 1) = n + m - (r - i)

    len=rilen = r - i 表示 bb 的这段连续子串的长度,那么 s=n+mlen|s| = n + m - len

    因此,问题转化为:最大化 lenlen,即 bb 中能作为 aa 的子序列的最长连续子串的长度


    算法实现

    我们可以枚举 bb 的每一个起始位置 ii1im1 \le i \le m),然后用贪心的方式匹配 aa:从 aa 的第一个字符开始扫描,每当遇到与 bb 当前所需字符相同时,就将 bb 的指针右移。这样能匹配到的最大长度就是从 ii 开始能匹配的最长连续段。

    具体过程:

    • 外层循环枚举 ii 作为 bb 的起始位置。
    • 内层两个指针 ll(遍历 aa)和 rr(初始 =i=i,表示 bb 中当前待匹配位置):
      • a[l]=b[r]a[l] = b[r],则 rr 右移(匹配成功);
      • ll 始终右移(扫描 aa)。
    • ll 超过 nnrr 超过 mm 时停止,记录 len=rilen = r - i

    对所有 ii 取最大的 lenlen,答案即为 n+mmax(len)n + m - \max(len)

    时间复杂度:对于每组测试数据,外层 O(m)O(m),内层 O(n)O(n),总计 O(nm)O(nm)t103t \le 10^3n,m100n,m \le 100,总计算量约 10710^7,可以轻松通过。


    参考代码

    #include <iostream>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
        
        int T;
        cin >> T;
        while (T--) {
            string a, b;
            cin >> a >> b;
            int n = a.size(), m = b.size();
            // 在前面插入一个占位符,使得下标从 1 开始
            a = ' ' + a;
            b = ' ' + b;
            
            int max_len = 0;
            for (int i = 1; i <= m; ++i) {
                int l = 1, r = i;          // l 扫描 a,r 扫描 b
                while (l <= n && r <= m) {
                    if (a[l] == b[r]) ++r; // 匹配成功,b 指针右移
                    ++l;                   // a 指针始终右移
                }
                max_len = max(max_len, r - i);
            }
            cout << n + m - max_len << '\n';
        }
        return 0;
    }
    • 1

    信息

    ID
    6888
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者