8 收心赛1 T1 剪切字符串 题解

剪切字符串

题面

给定三个长度为 \(n\)仅由小写字母构成的字符串 \(a,b,c\) ,要求选两个分界点 \(i,j\)

最小化

\[lcp(a_{1...i},b_{i+1...j}) + lcp(b_{i + 1...j}, c_{j + 1 ... n}) + lcp(a_{1 ... i},c_{j + 1 ... n}) \]

求出这个最小值

\(3 \le n \le 10^5\) ,保证字符串中仅有小写字母

题解

下面我们把划分完的字符串也叫做 \(a,b,c\)

因为 \(lcp\) 是两个字符串的最长公共前缀,所以 \(lcp\) 的最大值为两个字符串中的较小值

那么如果我们让 \(i = n - 2\)\(j = n - 1\) ,那么

\(lcp(a,b) \le 1,lcp(b,c) \le 1 ,lcp(b,c) \le 1\) ,所以 \(ans \le 3\)

那么我们只需要考虑 \(ans < 3\) 的可能情况即可

一个比较直接的想法是枚举 \(i,j\) 暴力计算,但这样的时间 复杂度是不被允许的,考虑优化

假设我们已经枚举了一个 \(i\)

\(len_b \le 3\) 我们可以暴力枚举 \(j = i + 1,i + 2, i + 3\) 计算答案

否则,这时 \(j\) 的位置已经不会对 \(lcp(a,b)\) 产生什么影响(因为答案最大为 3),所以我们只需要考虑 \(j\) 的位置对 \(lcp(a,c)\)\(lcp(b,c)\) 的影响

如果我们能够找到一个 \(c_{j + 1} \not= a_1 \land c_{j + 1} \not= b_{i + 1}\) ,那么 \(lcp(a,c) + lcp (b,c) = 0\)

否则

如果 \(a_1 \not= b_{i + 1}\) 后面无论怎么取,答案一定 \(\ge 1\) ,所以我们取 \(j = n - 1\) 时得到答案为 1,为最优

如果 \(a_1 = b_{i + 1}\) 后面的 \(c_{j + 1}\) 都和这两个相等,答案至少为 2 ,所以我们取 \(j = n - 1\) 还是最优解

那么整体的算法流程也比较清晰了,\(j\) 一定在 \(i + 1, i + 2, i + 3, n - 1\) 范围内

首先,枚举一个 \(i\) 然后暴力计算 \(j = i + 1, i + 2, i + 3\) 的答案

然后根据能够找到符合条件的 \(c_{j + 1}\) 分类讨论即可

code

代码实现还是有些细节的,要学习一下标程的实现思路

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
#include <set>

using namespace std;

const int N = 1e5 + 10;

int n;
char a[N], b[N], c[N];
map <char, int> cnt;
set <char> tot;

void ins (char ch) {
    if (++cnt[ch] == 1) tot.insert (ch);
}
void del (char ch) {
    if (--cnt[ch] == 0) tot.erase (ch);
}

//查看有没有符合条件的 c_i
bool check (char c1, char c2) {
    for (char c : tot) {
        if (c != c1 && c != c2) {
            return true;
        }
    }
    return false;
}

//计算两个字符串的lcp
int mat (char s1[], char s2[], int i, int j, int k) {
    int d = 0;
    while (i + d < j && j + d < k && d < 3) {
        if (s1[i + d] != s2[j + d]) break;
        d ++;
    }
    return d;
}

//计算分界点为 i j 时的答案
int calc (int i, int j) {
    int res = mat (a, b, 1, i + 1, j + 1) + mat (b, c, i + 1, j + 1, n + 1) + mat (a, c, 1, j + 1, n + 1);
    return res;
}

int main () {
    // freopen ("lcp.in", "r", stdin);
    // freopen ("lcp.out", "w", stdout);

    int T;
    cin >> T;
    while (T--) {
        scanf ("%d%s%s%s", &n, a + 1, b + 1, c + 1);

        int ans = (a[1] == b[n - 1]) + (a[1] == c[n]) + (b[n - 1] == c[n]);

        for (int i = 6; i <= n; i++) 
            ins (c[i]);

        for (int i = 1; i + 1 < n; i++) {
            for (int c = 1; i + c < n && c <= 3; c++) {
                ans = min (ans, calc (i, i + c));
            }
            int mat1 = mat (a, b, 1, i + 1, n + 1);
            if (check (b[i + 1], a[1])) ans = min (ans, mat1);
            else ans = min (ans, mat1 + (c[n] == b[i + 1]) + (a[1] == c[n]));
            del (c[i + 5]);
        }

        cout << ans << endl;
    }

    return 0;
}
posted @ 2025-08-28 14:13  michaele  阅读(37)  评论(0)    收藏  举报