1 条题解

  • 0
    @ 2026-5-17 14:52:33

    题目 C. Saraga 详细题解

    问题重述

    给定两个字符串 SSTT,要求构造一个最短的字符串 ZZ,使得存在至少两种不同的方式将 ZZ 分割成两个非空子串 AABB(即 Z=A+BZ = A + B),其中 AASS 的前缀,BBTT 的后缀。若不存在则输出 1-1

    关键转化

    对于任意一种分割方式,设分割点位于第 kk 个字符之后,即 A=Z[1..k]A = Z[1..k]SS 的前缀,B=Z[k+1..Z]B = Z[k+1..|Z|]TT 的后缀。令 P=Z[1..k1]P = Z[1..k-1](可能为空?但要求 AA 非空,所以 k1k \ge 1BB 非空,所以 k<Zk < |Z|;而 PP 可以为空,但后续需要 PP 非空?实际上我们后面会要求 PP 非空,但这里先保留)。再令 Q=Z[k]Q = Z[k](单个字符),R=Z[k+1..Z]R = Z[k+1..|Z|],则:

    • A=P+QA = P + QSS 的前缀,
    • B=RB = RTT 的后缀,且 Q+RQ + R 也是 TT 的后缀(因为 Q+R=Z[k..Z]Q+R = Z[k..|Z|]BB 加上前面一个字符,不一定是后缀?注意,Q+RQ+R 实际上是 ZZkk 开始的后缀,而 BB 是从 k+1k+1 开始,所以 Q+RQ+R 不一定是 TT 的后缀。但我们可以换一种方式:另一种常见的分解是 Z=P+(Q+R)Z = P + (Q+R),其中 Q+RQ+RTT 的后缀。所以实际上,对于一个分割点 kk,我们可以定义三元组 (P,Q,R)(P, Q, R) 满足:
      • Z=P+Q+RZ = P + Q + R
      • P+QP+QSS 的前缀,
      • Q+RQ+RTT 的后缀,
      • Q=1|Q| = 1(因为 QQ 恰好是分割点处的单个字符)。

    因此每一种分割方式都对应一个单字符 QQ

    化简到 Q=1|Q|=1

    题目要求至少两种不同的分割方式,即存在两个不同的单字符位置 c1c_1c2c_2(可能相同字符但位置不同),使得相应的条件成立。可以证明(见标程推导),任何有趣的缩写都可以通过不断将 QQ 的最后一个字符移到 RR 前面,最终得到一个 Q=1|Q|=1 的表示,且该表示仍然具有至少两种分割方式。因此,我们只需要考虑 Q=1|Q|=1 的情况。

    构造方法

    对于任意一个字符 cc,假设存在一个 SS 的前缀 PcPc(长度 2\ge 2)以 cc 结尾,并且存在一个 TT 的后缀 cRcR(长度 2\ge 2)以 cc 开头。那么构造

    Z=(Pc)+RZ = (Pc) + R

    其中 PcPcSS 的前缀(长度为 ii),cRcRTT 的后缀(长度为 jj),则 RR 的长度为 j1j-1。于是 Z=i+j1|Z| = i + j - 1

    这个 ZZ 自动具有两种不同的分割方式:

    1. 分割点在 PcPc 的末尾:A=PcA = PcSS 的前缀),B=RB = RTT 的后缀,因为 cRcR 是后缀,去掉第一个字符 ccRR 仍是后缀)。
    2. 分割点在 PcPc 去掉最后一个字符后的位置:设 Pc=P+cPc = P' + c,则 PP'SS 的前缀(长度 i11i-1 \ge 1),且 cRcRTT 的后缀,于是 A=PA = P'B=cRB = cR,两者非空且满足条件。

    因此,只要存在这样的 cc,就能构造出一个有趣的缩写。为了得到最短的 ZZ,我们只需对每个 cc,取 SS最短的cc 结尾的前缀(即最小的 i2i \ge 2 使得 S[i]=cS[i]=c),以及 TT最短的cc 开头的后缀(即最小的 j2j \ge 2 使得 TT 的倒数第 jj 个字符为 cc),然后计算长度 i+j1i+j-1,取所有 cc 中的最小值即可。

    算法步骤

    1. 初始化两个数组 pref[26]\text{pref}[26]suf[26]\text{suf}[26] 为无穷大。
    2. 遍历 SS(下标从 11 开始),对于 i=2i = 2S|S|,令 c=S[i]c = S[i],更新 pref[c]=min(pref[c],i)\text{pref}[c] = \min(\text{pref}[c], i)
    3. 遍历 TT,对于 j=T1j = |T|-111(即从倒数第二个字符向前),令 c=T[j]c = T[j],后缀长度 len=Tj+1len = |T| - j + 12\ge 2),更新 suf[c]=min(suf[c],len)\text{suf}[c] = \min(\text{suf}[c], len)
    4. 枚举 c=0c = 02525,若 pref[c]\text{pref}[c]suf[c]\text{suf}[c] 均非无穷,则候选长度 L=pref[c]+suf[c]1L = \text{pref}[c] + \text{suf}[c] - 1,记录最小 LL 及对应的 cc
    5. 若无任何候选,输出 1-1;否则,输出 $S[1..\text{pref}[c]] + T[|T|-\text{suf}[c]+2 .. |T|]$(即 SS 的前 pref[c]\text{pref}[c] 个字符拼接 TT 的最后 suf[c]1\text{suf}[c]-1 个字符)。

    复杂度分析

    • 预处理 O(S+T)O(|S|+|T|)
    • 枚举 2626 个字母 O(1)O(1)
    • 总时间复杂度 O(S+T)O(|S|+|T|),满足 S,T2×105|S|,|T| \le 2\times 10^5 的要求。

    边界情况

    • 如果 S=1|S|=1T=1|T|=1,则无法找到长度 2\ge 2 的前缀或后缀,输出 1-1
    • 注意字符串下标从 00 开始时的实现细节。

    参考代码(C++)

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        string S, T;
        cin >> S >> T;
        int n = S.size(), m = T.size();
        const int INF = 1e9;
        vector<int> pref(26, INF), suf(26, INF);
        // 最短前缀(长度>=2)以某字母结尾
        for (int i = 1; i < n; ++i) { // i是索引,对应长度i+1
            int c = S[i] - 'a';
            pref[c] = min(pref[c], i+1); // 长度 = i+1
        }
        // 最短后缀(长度>=2)以某字母开头
        for (int j = m-2; j >= 0; --j) {
            int c = T[j] - 'a';
            int len = m - j; // 从j到末尾的长度
            suf[c] = min(suf[c], len);
        }
        int best_len = INF;
        char best_c = -1;
        for (int c = 0; c < 26; ++c) {
            if (pref[c] != INF && suf[c] != INF) {
                int len = pref[c] + suf[c] - 1;
                if (len < best_len) {
                    best_len = len;
                    best_c = c;
                }
            }
        }
        if (best_c == -1) {
            cout << -1 << endl;
        } else {
            string ans = S.substr(0, pref[best_c]) + T.substr(m - (suf[best_c] - 1));
            cout << ans << endl;
        }
        return 0;
    }
    
    • 1

    信息

    ID
    7154
    时间
    1000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者