1 条题解

  • 0
    @ 2025-8-24 23:16:42

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 一扶苏一
    休息结束。邮箱 yifusuyi@qq.com

    搬运于2025-08-24 23:16:42,当前版本为作者最后更新于2025-05-24 19:34:27,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    一句话题解:

    考虑枚举答案串在 ss 里开头的位置 sis_i。然后把 siss_{i \sim |s|} 这个后缀扔到 tt 的子序列自动机上做匹配,那么能匹配上的长度就是以该位置开头的最长所求串。

    更细致的题解:

    考虑枚举 ss 的一个子串 ss',然后检查 ss' 能否成为 tt 的子序列。这是一个经典问题,可以使用子序列自动机解决。

    具体的,考虑把 ss' 的每一位从前向后在 tt 上进行匹配。假设 ss' 的前 ii 位是 tt 的前 jj 位的子序列,根据贪心的原则,显然 jj 越小越好(这样 ss' 后面的位置可以匹配更多的字符)。因此假设当前 ss' 的前 ii 位匹配了 tt 的前 jj 位,那么 ss' 的第 i+1i + 1 位应该匹配 tjt_j 后面第一个 si+1s_{i + 1}' 出现的位置。

    这样我们需要维护出 tt 里每个位置的下一个字符是什么,放在代码的 ff 数组里,ff 就是 tt 的子序列自动机。每次只需要把 ss' 放在自动机上跑,没有跑出自动机就说明 ss'tt 的子串。

    进一步的,对于 ss 的一个后缀 suf(s)suf(s),它能在 tt 的子序列自动机上被接受的最长前缀长度就是这个后缀能匹配上的最长子串。

    所以枚举 ss 的后缀,对每个后缀都放到子序列自动机上跑一次就行了。时间复杂度 O(s2+Σt)O(|s|^2 + |\Sigma||t|)

    #include <bits/stdc++.h>
    
    int main() {
      int T;
      for (std::cin >> T; T; --T) {
        std::string s, t;
        std::cin >> s >> t;
        t = " " + t;
        std::vector f(t.size() + 1, std::vector<int>(26, -1));
        for (int i = t.size() - 1; i; --i) {
          f[i - 1] = f[i];
          f[i - 1][t[i] - 'a'] = i;
        }
        std::string ans;
        for (int i = 0; i < s.size(); ++i) {
          int j = i, p = 0;
          while (j < s.size()) {
            int ch = s[j] - 'a';
            if (f[p][ch] == -1) break;
            p = f[p][ch];
            ++j;
          }
          int len = j - i;
          if (len > ans.length()) {
            ans = s.substr(i, len);
          } else if (len == ans.length()) {
            auto cur = s.substr(i, len);
            if (cur < ans) ans = cur;
          }
        }
        std::cout << ans << std::endl;
      }
    }
    
    • 1

    信息

    ID
    12305
    时间
    1000ms
    内存
    512MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者