• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【子序列自动机+脑筋急转弯】codeforce 2014 E. Unpleasant Strings

题目

https://codeforces.com/problemset/problem/2104/E

题解

将该问题分解为两个子问题:

  1. 如何快速(从左到右)找到最短子串,满足该子串可以匹配出子序列?
  2. 如何在匹配完子序列后的子串,计算出不存在的最短子序列长度?

对于子问题 1,可以考虑将 \(26\) 个字母的下标分别放在一个桶里,然后二分匹配出子序列。若匹配不到,则说明字符串 \(t\) 自身就不是字符串 \(s\) 的子序列,答案为 \(0\);若完全可以匹配,则需要思考子问题 2。

对于子问题 2,可以参考问题:https://leetcode.cn/problems/shortest-impossible-sequence-of-rolls/description/
不妨思考,一个字符串能匹配只包含前 \(k\) 个字母的长度为 \(len\) 的任意子序列满足具备什么性质?
将长度为 \(len\) 的子序列划分为 \(len\) 个部分:

想要让任意只包含前 \(k\) 个字母的长为 \(len\) 的子序列,都能在一个字符串中匹配上,则这 \(len\) 块,明显都需要满足一个性质:每一块都能出现前 \(k\) 个字符至少一次。

因此,可以逆序预处理出字符串 \(s\) 所有下标位置不能匹配的最短子序列长度。

参考代码

#include<bits/stdc++.h>

#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define eb(x) emplace_back(x)
using namespace std;

constexpr int N = 1e6 + 7;
int T = 1, n, m, w, cnt;
string s, t;
vector<int> v[26], mark(26);
int a[N];// 预处理维护答案

int main() {
    IOS
    cin >> n >> m >> s >> w;
    for (int i = 0; i < n; ++ i) v[s[i] - 'a'].eb(i);// 用桶维护每个字母的下标序列
    int res = 1;// 匹配不上的最短长度
    a[n] = 1;// 匹配完子序列,字符串 s 无剩余字符,只需要添加一个字符即可使得子序列不再匹配
    for (int i = n - 1; i >= 0; -- i) {
        int idx = s[i] - 'a';
        if (mark[idx] < res) {
            ++ mark[idx];
            if (++ cnt == m) {
                cnt = 0;
                ++ res;
            }
        }
        a[i] = res;
    }
    while (w --) {
        cin >> t;
        int len = t.size(), ans = 0;
        if (len <= n) {
            int j = -1;
            for (int i = 0; i < len; ++ i) {
                int loc = t[i] - 'a';
                auto it = upper_bound(v[loc].begin(), v[loc].end(), j);// 二分匹配出字符串 s 剩余子串第一个字符 t[i] 的位置
                if (it == v[loc].end()) {
                    j = n;// 匹配的位置已经越界,即无法在字符串 s 中剩余子串匹配上
                    break;
                }
                j = *it;// 匹配上合理的位置
            }
            ans = a[j + 1];
        }
        cout << ans << '\n';
    }
    return 0;
}

posted on 2025-06-21 16:49  RomanLin  阅读(14)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3