Dividing Strings

链接:https://ac.nowcoder.com/acm/contest/5669/D
来源:牛客网

With powerful decision-making skills, ZYB can solve complex problems

ZYB has a decimal string s {s} s of length n {n} n. He wants to split the string into several (at least 2 2 2) non-empty continuous substrings and minimize the difference between the maximum value and the minimum value of the gifts. The value of a continuous substring is the value of it when it’s considered as a decimal number.
For instance, when the string ‘1230’ is split into ‘12’ and ‘30’, the values of the two parts are 12 and 30 separately. Notice that the substrings are not allowed to contain leading zero(s). For example, the only feasible way of splitting ‘001’ is ‘0’, ‘0’ and ‘1’.

Can you help ZYB find the fairest splitting?
输入描述:
There are multiple test cases. The first line of input contains an integer T {T} T, indicating the number of test cases. For each test case:
The first line contains an integer n {n} n ( 2 ≤ n ≤ 1 0 5 2 \le n \le 10^5 2n105), indicating the length of the decimal string.
The second line contains a decimal string s {s} s of length n {n} n.( ’0’ ≤ s [ i ] ≤ ’9’ \text{'0'} \le s[i] \le \text{'9'} ’0’s[i]’9’ for each 1 ≤ i ≤ n 1 \le i \le n 1in).
It is guaranteed that the sum of n {n} n in all test cases does not exceed 1 0 6 10^6 106.
输出描述:
For each test case, output one line containing the minimum difference between the maximum value and the minimum value.
示例1

输入
4
2
08
5
10199
7
9710296
8
12341234
输出
8
2
6
0

分两种情况讨论。
第一种情况是所有划分的子串长度相等,从小到大枚举循环节的长度 i i i,如果 n m o d    i = 0 n\mod i=0 nmodi=0,则在所有循环节中找出最大的和最小的,然后最大减最小更新 a n s ans ans,需要用到大数相减和大数比较。时间复杂度为 O ( n n ) O(n\sqrt n) O(nn )。因为不能有前导 0 0 0,实际跑不满这个复杂度。
第二种情况是划分的子串不相等。因为最后一定 a n s ≤ 9 ans≤9 ans9,因此子串的长度相差 1 1 1,且较长的子串为 100 … 0 x 100\dots0x 1000x;较短的子串为 99 … 9 y 99\dots9y 999y,枚举子串长度判断是否子串可以划分为这两种形式,若能够划分,则用 10 + x − y 10+x-y 10+xy来更新 a n s ans ans。观察可发现全 0 0 0子串和全 1 1 1子串求和比较特殊,因此可以用前缀和来实现 O ( 1 ) O(1) O(1)判断,复杂度为 O ( n H n ) O(nH_n) O(nHn),实际子串很难全部划分为仅这两种类型,因此复杂度接近 O ( n ) O(n) O(n)
最终时间复杂度为 O ( n n ) O(n\sqrt n) O(nn )

#include <bits/stdc++.h>

#define si(a) scanf("%d",&a)
#define sl(a) scanf("%lld",&a)
#define sd(a) scanf("%lf",&a)
#define sc(a) scanf("%c",&a)
#define ss(a) scanf("%s",a)
#define pi(a) printf("%d\n",a)
#define pl(a) printf("%lld\n",a)
#define pc(a) putchar(a)
#define ms(a) memset(a,0,sizeof(a))
#define repi(i, a, b) for(register int i=a;i<=b;++i)
#define repd(i, a, b) for(register int i=a;i>=b;--i)
#define reps(s) for(register int i=head[s];i;i=Next[i])
#define ll long long
#define ull unsigned long long
#define vi vector<int>
#define pii pair<int,int>
#define mii unordered_map<int,int>
#define msi unordered_map<string,int>
#define lowbit(x) ((x)&(-(x)))
#define ce(i, r) i==r?'\n':' '
#define pb push_back
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define pr(x) cout<<#x<<": "<<x<<endl
using namespace std;

inline int qr() {
    int f = 0, fu = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')fu = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        f = (f << 3) + (f << 1) + c - 48;
        c = getchar();
    }
    return f * fu;
}

const int N = 1e5 + 10;
char str[N];
int a[N], s[N], b[N], T, n, ans;

inline bool cmp(int x, int y, int l) {
    repi(i, 0, l - 1)if (a[x + i] != a[y + i])return a[x + i] < a[y + i];
    return false;
}

inline void sub(int x, int l) {
    repi(i, 1, l)b[i] -= a[x + i - 1];
    repd(i, n, 1)if (b[i] < 0)b[i - 1]--, b[i] += 10;
}

inline int solve1(int l) {
    int mni = 1, mxi = 1;
    for (int i = 1; i <= n; i += l) {
        if (l > 1 && !a[i])return 9;
        if (cmp(i, mni, l))mni = i;
        else if (cmp(mxi, i, l))mxi = i;
    }
    memcpy(b + 1, a + mxi, sizeof(int) * (l + 10));
    sub(mni, l);
    repi(i, 1, l - 1)if (b[i])return 9;
    return b[l];
}

inline int solve2(int l) {
    int pos = 1, val1 = 9, val2 = 0;
    while (pos <= n) {
        if (a[pos] == 1) {
            if (pos + l > n || s[pos + l - 1] - s[pos])return 9;
            val2 = max(val2, a[pos + l]), pos += l + 1;
        } else {
            if (pos + l - 1 > n || s[pos + l - 2] - s[pos - 1] != 9 * (l - 1))
                return 9;
            val1 = min(val1, a[pos + l - 1]), pos += l;
        }
    }
    return 10 - val1 + val2;
}

int main() {
    T = qr();
    while (T--) {
        n = qr();
        ss(str + 1);
        ans = 9;
        repi(i, 1, n)a[i] = str[i] - '0', s[i] = s[i - 1] + a[i];
        repi(i, 1, n / 2) {
            ans = min(ans, solve2(i));
            if (n % i == 0)ans = min(ans, solve1(i));
        }
        pi(ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_sky123_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值