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;
}
posted @ 2025-10-05 17:35  michaele  阅读(7)  评论(0)    收藏  举报