16 LCA模拟赛1T1 密码 题解
密码
题面
给定两个由字符 \(0 \sim 9\) 组成的字符串 \(s, t\) ,\(t\) 是由 \(s\) 中的一段非空连续子串替换为其各个字符的和得到的
现在要求这一段非空连续子串的左右端点,下标从 1 开始
例如
input:
2148
213
output:
2 4
\(1 \le |s| \le 10^5\)
题解
这道题其实还算比较简单,不过我码力太弱,导致赛时写了1.5h
一个关键的观察:替换后的字符和长度不会超过 6 ,因为最多 \(10^5\) 个数,每个数最大是 \(9\) 那么最大数字和就是 \(9 \times 10^5\) 也就是 6 位
那么我们将 \(t\) 分成三段,前面不变的一段,中间被替换的段,后面不变的一段
假如我们知道了中间段的长度,那么也就能够推出 \(s\) 中的左右端点
我们只需要判断前面、后面两个字符串是否相等,以及 \(s\) 中被替换的一段替换后是否和我们枚举的这一段匹配即可
判断前后缀是否相等,可以预处理。快速求 \(s\) 中某一段的区间和,可以用前缀和算
时间复杂度 \(O(36 n)\) 如果交换一下枚举顺序,先枚举左端点,再枚举 \(len\) ,就可以省去一个 6 的常数
code
优化后代码,要注意前导零问题
namespace solution_a {
const int N = 1e5 + 10;
int n, m;
char s[N], t[N];
bool pre[N], suc[N];
int sum[N];
void solve () {
scanf ("%s%s", s + 1, t + 1);
n = strlen (s + 1), m = strlen (t + 1);
for (int i = 1; i <= n; i ++) {
sum[i] = sum[i - 1] + s[i] - '0';
}
// 预处理 pre & suc
int p = 1;
while (p <= m && s[p] == t[p]) pre[p ++] = 1;
p = 1;
while (p <= m && s[n - p + 1] == t[m - p + 1]) suc[p ++] = 1;
pre[0] = suc[0] = 1;
// 枚举,计算
for (int l = 1; l <= m; l ++) {
int sm = 0;
for (int r = l; r <= min (l + 5, m); r ++) {
if (r > l && !sm) break; // t 中被替换的这段数不能含有前导零
sm = sm * 10 + t[r] - '0';
if (suc[m - r] && pre[l - 1] && sm == sum[n - (m - r)] - sum[l - 1]) {
cout << l << ' ' << n - (m - r) << endl;
return;
}
}
}
}
}
赛时代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1e5 + 10;
int n, m;
char s[N], t[N];
bool pre[N], suc[N];
int sum[N];
int main () {
// freopen ("password.in", "r", stdin);
// freopen ("password.out", "w", stdout);
scanf ("%s%s", s + 1, t + 1);
n = strlen (s + 1), m = strlen (t + 1);
for (int i = 1; i <= n; i ++) {
sum[i] = sum[i - 1] + s[i] - '0';
}
for (int i = 1; i <= m; i ++) {
if (s[i] == t[i]) pre[i] = 1;
else {
break;
}
}
for (int i = n, j = m, cnt = 1; j >= 1 && i >= 1; i --, j --, cnt ++) {
if (s[i] == t[j]) suc[cnt] = 1;
else {
break;
}
}
pre[0] = suc[0] = 1;
for (int len = 1; len <= 10; len ++) {
for (int l = 1; l + len - 1 <= m; l ++) {
int r = l + len - 1;
int pw = 0;
char tmp[20];
int x = sum[n - (m - r)] - sum[l - 1];
if (!x) {
tmp[ ++ pw] = '0';
} else {
while (x) {
tmp [ ++ pw] = x % 10 + '0';
x /= 10;
}
reverse (tmp + 1, tmp + 1 + pw);
}
if (!suc[m - r] || !pre[l - 1]) continue;
bool ok = 1;
for (int j = 1; j <= pw; j ++) {
if (tmp[j] != t[l + j - 1]) {
ok = 0;
break;
}
}
if (ok) {
cout << l << ' ' << n - (m - r) << endl;
return 0;
}
}
}
return 0;
}