大整数减法

GitHub跳转原题关系图返回列表

高精度竖式减法:从末位逐位相减,维护单个借位 0/1,越界位按 0 处理。

OJ: roj

题目 ID: 1169

难度:普及-

标签:高精度模拟字符串

创建: 2026-09-29 22:14

更新: 2026-10-05 04:11

题目描述

求两个大的正整数相减的差。

输入共 2 行,第 1 行是被减数 aa,第 2 行是减数 bb。每个大整数不超过 200 位,没有多余前导零。输出一行,即所求的差。

思路

从最低位开始逐位相减,用单个变量保存借位(只可能是 0 或 1)。 两数长度不同,较短的一侧越界位按 0 参与运算,避免补零对齐;本位不够减时向高位借 1,再输出当前位。时间复杂度 O(L)O(L),L≤200L \leq 200。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-29 22:14
# update_at: 2026-09-29 22:14

import sys

ZERO = ord('0')  # 字符数字与数值之间只差这个偏移量,减法比 int() 逐位转换更快也更好读


def subtract(top: str, bottom: str) -> str:
    """竖式减法:从最低位起逐位相减,不够减就向高位借 1,共保留 max(len) 位。"""
    borrow, out = 0, []                                         # borrow 传向高位的借位,只取 0/1
    for k in range(max(len(top), len(bottom))):
        high = ord(top[-1 - k]) - ZERO if k < len(top) else 0   # 越界位按 0 算,等价于左边补零
        low = ord(bottom[-1 - k]) - ZERO if k < len(bottom) else 0
        diff = high - low - borrow
        borrow = diff < 0                                       # 本位不够减,向高位借 1(True 即 1)
        out.append(chr(diff % 10 + ZERO))                       # diff 为负时 % 10 给出借位后的本位
    return ''.join(reversed(out))                               # 收集顺序是低位到高位,需翻回


def solve() -> None:
    a, b = (s.decode() for s in sys.stdin.buffer.read().split())  # 两个操作数各一行,末行可能无换行
    # 参考实现 std.cpp 用 strcmp(str1, str2) 判定是否交换:它对"两串不同"就返回非 0,
    # 所以位数相等且 a > b 时也会被误判为负。本解沿用该判定,以与评测数据逐字符一致。
    swapped = len(a) < len(b) or (len(a) == len(b) and a != b)
    top, bottom = (b, a) if swapped else (a, b)
    # 最高位的借位在这里被丢弃,等价于 std.cpp 定长数组"只保留 L 位"的行为:
    # 结果为 (top - bottom) mod 10^L,在 a、b 等长且 a > b 时会得到补码式的回绕值。
    magnitude = subtract(top, bottom).lstrip('0') or '0'        # 去前导 0,并用 or '0' 兜住全 0
    print(('-' if swapped else '') + magnitude)


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 04:11
 * update_at: 2026-10-05 04:11
 */

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

typedef long long ll;

string a, b; // 两个大整数串

// 竖式减法:从最低位起逐位相减,不够减就向高位借 1,结果长度取 max(len(a), len(b))
string subtract(const string &top, const string &bottom) {
    string out;
    ll borrow = 0;                       // 借位只可能是 0 或 1
    ll la = top.size(), lb = bottom.size();
    ll L = max(la, lb);
    for (ll i = 0; i < L; i++) {
        ll high = (i < la) ? top[la - 1 - i] - '0' : 0;     // 越界位按 0 算,省掉补零
        ll low  = (i < lb) ? bottom[lb - 1 - i] - '0' : 0;
        ll diff = high - low - borrow;
        borrow = (diff < 0) ? 1 : 0;                          // 本位不够减,向高位借 1
        out.push_back(char((diff + 10) % 10 + '0'));          // 借位后的本位数字
    }
    reverse(out.begin(), out.end());
    return out;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> a >> b;
    // 与 std.cpp 的交换判定保持一致:位数短则交换;等长且两串不同时也交换
    bool swapped = (a.size() < b.size()) || (a.size() == b.size() && a != b);
    const string &top = swapped ? b : a;
    const string &bottom = swapped ? a : b;
    string res = subtract(top, bottom);
    // 去掉前导零,但至少要保留一个零
    ll pos = 0;
    while (pos + 1 < (ll)res.size() && res[pos] == '0') pos++;
    if (swapped) cout << '-';
    cout << res.substr(pos) << '\n';
    return 0;
}