华为校招笔试真题 10.28 - 稀疏整数求和(C++/Py/Java /Js/Go)

稀疏整数求和

华为笔试真题 10月28号 第二题 200分题型

题目描述

给定两个位数可能极多的非负整数,请求出它们相加后的结果。

这类整数的十进制表示通常只有少量数位不为 000,所以仅记录非零数字片段。每个片段写作 c:p,其中 ccc 是片段系数,ppp 是十进制位权的指数。

一个 c:p 片段所贡献的数值为 c×10pc \times 10^pc×10p;ppp 对应系数最右侧数字所在的数位。

表示方式需要遵守以下约定:

  1. 指数满足 0≤p<2 000 000 0000 \le p < 2\,000\,000\,0000≤p<2000000000,允许的最大单项为 999:1999999999。
  2. 按数位从高向低列出片段,任何片段的系数中都不能出现数字 000;遇到零位时应断开片段。
  3. 系数 ccc 至多包含 333 个数字。若一段连续非零数字更长,就从左端开始,每次取尽可能多且不超过 333 位的数字作为一组。例如,8765432 应划为 876、543、2。
  4. 每个整数的编码末尾都附上标记 0:0,数值为零时只写这一标记。相邻项之间以空格隔开。
  5. 所有输入编码均合法。每行编码的字符数连同空格计入后小于 512512512,并保证求和结果中各片段的指数也小于 2 000 000 0002\,000\,000\,0002000000000。
    例如:
  • 十进制数 8765004009 的编码为 876:7 5:6 4:3 9:0 0:0。
  • 十进制数 8765004000 的编码为 876:7 5:6 4:3 0:0。

输入描述

  • 首行给出其中一个非负整数的编码。
  • 次行给出另一个非负整数的编码。
    两行均采用上述片段表示方式。

输出描述

输出这两个数相加得到的数值。

结果也须按上述约定编码:连续非零数字从高位起每次最多取 333 位,并在末尾附上 0:0。

样例1

输入:

复制代码
567:3 89:1 0:0
444:3 321:0 0:0

输出:

复制代码
1:6 122:4 11:0 0:0

解释:

两行分别代表 567890567890567890 和 444321444321444321,相加可得 101221110122111012211。

零位将结果分为 1 与 12211 两段;后一段再从左向右划为 122、11,故得到上面的编码。

样例2

输入:

复制代码
999:3 999:0 0:0
1:0 0:0

输出:

复制代码
1:6 0:0

解释:

相加的数值是 999999999999999999 与 111,得到 100000010000001000000。连续进位后,只有指数为 666 的数位非零,编码因此只包含这一片段和结束标记。

题解和思路

思路

实现思路:模拟

  1. 解析输入编码,使用哈希表存储每个非零数位,哈希表中key:value,其中
    • 键:十进制数位的位置
    • 值:该位置上的数字
  2. 从低位开始从低位开始模拟竖式加法,也是用哈希表进行存储。
  3. 将结果进行重新编码,利用得到的结果哈希表的非零数位后,从高位向低位扫描:
    1. 遇到连续非零数字时,按从左到右每组最多 3 位进行划分。
    2. 遇到零位时,结束当前片段。
    3. 每个片段输出为 c:p。
    4. 最后输出 0:0。
  4. 时间复杂度为O(KlogK)

C++

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

// 解析编码,将每个非零数位累加到对应位置
void parse(string line, map<ll, int>& digits) {
    stringstream ss(line);
    string token;

    while (ss >> token) {
        int k = token.find(':');
        string c = token.substr(0, k);
        ll p = stoll(token.substr(k + 1));

        if (c == "0") {
            continue;
        }

        // 从系数右侧开始拆分,每个数字对应一个十进制数位
        for (int i = (int)c.size() - 1; i >= 0; i--) {
            digits[p + (c.size() - 1 - i)] += c[i] - '0';
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    string line1, line2;
    getline(cin, line1);
    getline(cin, line2);

    // 存储两个整数对应数位的数字之和
    map<ll, int> digits;
    parse(line1, digits);
    parse(line2, digits);

    // 结果中非零数位的位置及对应数字
    map<ll, int> res;

    ll prev = -1;
    int carry = 0;

    // 从低位向高位模拟竖式加法
    for (auto [p, val] : digits) {
        // 如果中间存在零位,进位只会传递到紧邻的下一位
        if (prev != -1 && p > prev + 1 && carry > 0) {
            res[prev + 1] = carry;
            carry = 0;
        }

        int sum = val + carry;
        int digit = sum % 10;
        carry = sum / 10;

        if (digit != 0) {
            res[p] = digit;
        }

        prev = p;
    }

    // 处理最高位之后剩余的进位
    if (prev != -1 && carry > 0) {
        res[prev + 1] = carry;
    }

    if (res.empty()) {
        cout << "0:0\n";
        return 0;
    }

    // 从高位向低位遍历,将连续非零数位每三个划分为一组
    vector<pair<ll, int>> v(res.rbegin(), res.rend());
    vector<string> ans;

    string c = "";
    ll last = -1;


    for (auto [pos, digit] : v) {
        // 遇到不连续的数位,或者当前组已经有三位,则结束当前片段
        if (!c.empty() && (last - pos != 1 || c.size() == 3)) {
            ans.push_back(c + ":" + to_string(last));
            c.clear();
        }

        c += char('0' + digit);
        last = pos;
    }

    if (!c.empty()) {
        ans.push_back(c + ":" + to_string(last));
    }

    // 输出编码,并在末尾添加结束标记
    for (int i = 0; i < (int)ans.size(); i++) {
        if (i > 0) cout << ' ';
        cout << ans[i];
    }

    cout << " 0:0\n";
    return 0;
}

Java

java 复制代码
import java.util.*;
import java.io.*;

public class Main {
    // 解析编码,将每个非零数位累加到对应位置
    static void parse(String line, TreeMap<Long, Integer> digits) {
        String[] tokens = line.trim().split("\\s+");

        for (String token : tokens) {
            if (token.isEmpty()) {
                continue;
            }

            int k = token.indexOf(':');
            String c = token.substring(0, k);
            long p = Long.parseLong(token.substring(k + 1));

            if (c.equals("0")) {
                continue;
            }

            // 从系数右侧开始拆分,每个数字对应一个十进制数位
            for (int i = c.length() - 1; i >= 0; i--) {
                long pos = p + (c.length() - 1 - i);
                digits.put(pos, digits.getOrDefault(pos, 0) + c.charAt(i) - '0');
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String line1 = br.readLine();
        String line2 = br.readLine();

        // 存储两个整数对应数位的数字之和
        TreeMap<Long, Integer> digits = new TreeMap<>();
        parse(line1, digits);
        parse(line2, digits);

        // 结果中非零数位的位置及对应数字
        TreeMap<Long, Integer> res = new TreeMap<>();

        long prev = -1;
        int carry = 0;

        // 从低位向高位模拟竖式加法
        for (Map.Entry<Long, Integer> entry : digits.entrySet()) {
            long p = entry.getKey();
            int val = entry.getValue();

            // 如果中间存在零位,进位只会传递到紧邻的下一位
            if (prev != -1 && p > prev + 1 && carry > 0) {
                res.put(prev + 1, carry);
                carry = 0;
            }

            int sum = val + carry;
            int digit = sum % 10;
            carry = sum / 10;

            if (digit != 0) {
                res.put(p, digit);
            }

            prev = p;
        }

        // 处理最高位之后剩余的进位
        if (prev != -1 && carry > 0) {
            res.put(prev + 1, carry);
        }

        if (res.isEmpty()) {
            System.out.println("0:0");
            return;
        }

        // 从高位向低位遍历,将连续非零数位每三个划分为一组
        List<Map.Entry<Long, Integer>> v =
                new ArrayList<>(res.descendingMap().entrySet());

        List<String> ans = new ArrayList<>();
        StringBuilder c = new StringBuilder();
        long last = -1;

        for (Map.Entry<Long, Integer> entry : v) {
            long pos = entry.getKey();
            int digit = entry.getValue();

            // 遇到不连续的数位,或者当前组已经有三位,则结束当前片段
            if (c.length() > 0 && (last - pos != 1 || c.length() == 3)) {
                ans.add(c.toString() + ":" + last);
                c.setLength(0);
            }

            c.append((char) ('0' + digit));
            last = pos;
        }

        if (c.length() > 0) {
            ans.add(c.toString() + ":" + last);
        }

        // 输出编码,并在末尾添加结束标记
        System.out.println(String.join(" ", ans) + " 0:0");
    }
}

python

python 复制代码
import sys

# 解析编码,将每个非零数位累加到对应位置
def parse(line, digits):
    tokens = line.split()

    for token in tokens:
        k = token.index(':')
        c = token[:k]
        p = int(token[k + 1:])

        if c == "0":
            continue

        # 从系数右侧开始拆分,每个数字对应一个十进制数位
        for i in range(len(c) - 1, -1, -1):
            pos = p + (len(c) - 1 - i)
            digits[pos] = digits.get(pos, 0) + int(c[i])


def main():
    line1 = sys.stdin.readline().rstrip('\n')
    line2 = sys.stdin.readline().rstrip('\n')

    # 存储两个整数对应数位的数字之和
    digits = {}
    parse(line1, digits)
    parse(line2, digits)

    # 结果中非零数位的位置及对应数字
    res = {}

    prev = -1
    carry = 0

    # 从低位向高位模拟竖式加法
    for p in sorted(digits):
        val = digits[p]

        # 如果中间存在零位,进位只会传递到紧邻的下一位
        if prev != -1 and p > prev + 1 and carry > 0:
            res[prev + 1] = carry
            carry = 0

        s = val + carry
        digit = s % 10
        carry = s // 10

        if digit != 0:
            res[p] = digit

        prev = p

    # 处理最高位之后剩余的进位
    if prev != -1 and carry > 0:
        res[prev + 1] = carry

    if not res:
        print("0:0")
        return

    # 从高位向低位遍历,将连续非零数位每三个划分为一组
    v = sorted(res.items(), reverse=True)
    ans = []

    c = ""
    last = -1

    for pos, digit in v:
        # 遇到不连续的数位,或者当前组已经有三位,则结束当前片段
        if c and (last - pos != 1 or len(c) == 3):
            ans.append(c + ":" + str(last))
            c = ""

        c += str(digit)
        last = pos

    if c:
        ans.append(c + ":" + str(last))

    # 输出编码,并在末尾添加结束标记
    print(" ".join(ans) + " 0:0")


if __name__ == "__main__":
    main()

Javascript

js 复制代码
const readline = require('readline');

// 解析编码,将每个非零数位累加到对应位置
function parse(line, digits) {
    const tokens = line.trim().split(/\s+/);

    for (const token of tokens) {
        if (!token) {
            continue;
        }

        const k = token.indexOf(':');
        const c = token.substring(0, k);
        const p = BigInt(token.substring(k + 1));

        if (c === "0") {
            continue;
        }

        // 从系数右侧开始拆分,每个数字对应一个十进制数位
        for (let i = c.length - 1; i >= 0; i--) {
            const pos = p + BigInt(c.length - 1 - i);
            digits.set(pos, (digits.get(pos) || 0) + Number(c[i]));
        }
    }
}

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const lines = [];

rl.on('line', (line) => {
    lines.push(line);
});

rl.on('close', () => {
    const line1 = lines[0] || "";
    const line2 = lines[1] || "";

    // 存储两个整数对应数位的数字之和
    const digits = new Map();
    parse(line1, digits);
    parse(line2, digits);

    // 结果中非零数位的位置及对应数字
    const res = new Map();

    let prev = -1n;
    let carry = 0;

    // 从低位向高位模拟竖式加法
    const entries = Array.from(digits.entries())
        .sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);

    for (const [p, val] of entries) {
        // 如果中间存在零位,进位只会传递到紧邻的下一位
        if (prev !== -1n && p > prev + 1n && carry > 0) {
            res.set(prev + 1n, carry);
            carry = 0;
        }

        const sum = val + carry;
        const digit = sum % 10;
        carry = Math.floor(sum / 10);

        if (digit !== 0) {
            res.set(p, digit);
        }

        prev = p;
    }

    // 处理最高位之后剩余的进位
    if (prev !== -1n && carry > 0) {
        res.set(prev + 1n, carry);
    }

    if (res.size === 0) {
        console.log("0:0");
        return;
    }

    // 从高位向低位遍历,将连续非零数位每三个划分为一组
    const v = Array.from(res.entries())
        .sort((a, b) => a[0] > b[0] ? -1 : a[0] < b[0] ? 1 : 0);

    const ans = [];
    let c = "";
    let last = -1n;

    for (const [pos, digit] of v) {
        // 遇到不连续的数位,或者当前组已经有三位,则结束当前片段
        if (c.length > 0 && (last - pos !== 1n || c.length === 3)) {
            ans.push(c + ":" + last.toString());
            c = "";
        }

        c += String(digit);
        last = pos;
    }

    if (c.length > 0) {
        ans.push(c + ":" + last.toString());
    }

    // 输出编码,并在末尾添加结束标记
    console.log(ans.join(" ") + " 0:0");
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
)

// 解析编码,将每个非零数位累加到对应位置
func parse(line string, digits map[int64]int) {
	tokens := strings.Fields(line)

	for _, token := range tokens {
		k := strings.Index(token, ":")
		c := token[:k]
		p, _ := strconv.ParseInt(token[k+1:], 10, 64)

		if c == "0" {
			continue
		}

		// 从系数右侧开始拆分,每个数字对应一个十进制数位
		for i := len(c) - 1; i >= 0; i-- {
			pos := p + int64(len(c)-1-i)
			digits[pos] += int(c[i] - '0')
		}
	}
}

func main() {
	reader := bufio.NewReader(os.Stdin)

	line1, _ := reader.ReadString('\n')
	line2, _ := reader.ReadString('\n')

	line1 = strings.TrimSpace(line1)
	line2 = strings.TrimSpace(line2)

	// 存储两个整数对应数位的数字之和
	digits := make(map[int64]int)
	parse(line1, digits)
	parse(line2, digits)

	// 结果中非零数位的位置及对应数字
	res := make(map[int64]int)

	positions := make([]int64, 0, len(digits))
	for p := range digits {
		positions = append(positions, p)
	}
	sort.Slice(positions, func(i, j int) bool {
		return positions[i] < positions[j]
	})

	prev := int64(-1)
	carry := 0

	// 从低位向高位模拟竖式加法
	for _, p := range positions {
		val := digits[p]

		// 如果中间存在零位,进位只会传递到紧邻的下一位
		if prev != -1 && p > prev+1 && carry > 0 {
			res[prev+1] = carry
			carry = 0
		}

		sum := val + carry
		digit := sum % 10
		carry = sum / 10

		if digit != 0 {
			res[p] = digit
		}

		prev = p
	}

	// 处理最高位之后剩余的进位
	if prev != -1 && carry > 0 {
		res[prev+1] = carry
	}

	if len(res) == 0 {
		fmt.Println("0:0")
		return
	}

	// 从高位向低位遍历,将连续非零数位每三个划分为一组
	v := make([]int64, 0, len(res))
	for p := range res {
		v = append(v, p)
	}
	sort.Slice(v, func(i, j int) bool {
		return v[i] > v[j]
	})

	ans := make([]string, 0)
	c := ""
	last := int64(-1)

	for _, pos := range v {
		digit := res[pos]

		// 遇到不连续的数位,或者当前组已经有三位,则结束当前片段
		if len(c) > 0 && (last-pos != 1 || len(c) == 3) {
			ans = append(ans, c+":"+strconv.FormatInt(last, 10))
			c = ""
		}

		c += strconv.Itoa(digit)
		last = pos
	}

	if len(c) > 0 {
		ans = append(ans, c+":"+strconv.FormatInt(last, 10))
	}

	// 输出编码,并在末尾添加结束标记
	fmt.Println(strings.Join(ans, " ") + " 0:0")
}
相关推荐
无限码力2 小时前
华为校招笔试真题 10.28 - 运输中转站计数(C++/Py/Java /Js/Go)
华为·华为笔试真题·华为校招笔试真题·华为秋招笔试真题·华为校招算法笔试题
熊猫钓鱼>_>12 小时前
碰一碰无网近场文件快传与断点续传【鸿蒙心迹】
华为·交互·harmonyos·arkts·鸿蒙·openharmony·鸿蒙心迹
每天进步一点点km16 小时前
自用-牛客网-华为机试编程模拟题1-python
华为od·华为·面试
懿路向前17 小时前
唤醒工作台全实录(一):立项——化性起伪与三条设计原则
人工智能·华为
hhhjhl19 小时前
开源鸿蒙平台 KMP_CMP 三方库「Clikt」适配全流程
华为·开源·harmonyos
熊猫钓鱼>_>20 小时前
四种书签,一条时间线:一个读书 App 的记忆颗粒与它的边界案例【鸿蒙心迹】
开发语言·华为·ai编程·harmonyos·鸿蒙·openharmony·鸿蒙pc
Goway_Hui20 小时前
【KMP for OpenHarmony】-- 在鸿蒙工程中使用 Apollo-Kotlin
华为·kotlin·harmonyos
熊猫钓鱼>_>1 天前
鸿蒙 PC 三方库适配复盘:libfvad 1.0 —— 把 WebRTC VAD 做成能装进采集链路的静态库
服务器·开发语言·华为·harmonyos·openharmony·适配·鸿蒙pc
aqi001 天前
AI编程让鸿蒙开发电子书APP更加快捷
android·华为·harmonyos·鸿蒙·harmony