8 收心赛1 T1 剪切字符串 题解
剪切字符串
题面
给定三个长度为 \(n\) 的仅由小写字母构成的字符串 \(a,b,c\) ,要求选两个分界点 \(i,j\)
最小化
求出这个最小值
\(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;
}

浙公网安备 33010602011771号