密码
题面
给定两个由字符 \(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 & sucint 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;
}