引言:最近秋招黄金期,陆陆续续收到了一些笔试邀请,但很多都是ACM模式而不是力扣上的核心代码模式。所以代码题做起来无从下手,从网上找资料也很少有Javascript版本的ACM输入输出模板,并且也不够详细。于是自己搜集了许多资料,准备把常见的模板整理出来,希望可以帮到大家。
一、基础模板
我们先来看一个基础模板
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 直接输出需要的字符串,不需要处理输入
console.log("Hello Nowcoder!");
}()
我们接下来逐行解析下每行代码的作用
1. 引入 readline 模块并创建接口
js
const rl = require("readline").createInterface({ input: process.stdin });
require("readline")
:引入Node.js内置的readline
模块,这个模块用于从命令行(标准输入)读取一行一行的输入。createInterface({ input: process.stdin })
:创建一个输入接口,指定输入源为process.stdin
(标准输入,也就是用户在控制台输入的内容)。- 变量
rl
就是这个输入接口的实例,后续通过它来控制输入的读取。
2. 创建异步迭代器
js
var iter = rl[Symbol.asyncIterator]();
Symbol.asyncIterator
是Javascript的一个内置符号,用于定义对象的异步迭代器- 这里通过
rl[Symbol.asyncIterator]()
获取rl接口的异步迭代器,赋值给iter
。 - 异步迭代器的作用是:可以通过
next()
方法异步地获取下一行输入(因为输入是用户手动输入的,属于异步操作)。
3. 定义读取一行输入的函数
js
const readline = async () => (await iter.next()).value;
- 这是一个异步函数(
async
标记),作用是读取一行输入。 - 调用
iter.next()
会返回一个Promise,await
会等待这个Promise完成,获取下一行输入的结果。 - 结果的
value
属性就是读取到的一行字符串(如果没有更多输入,value
会是undefined
)。 - 简单说:调用
readline()
就可以得到一行输入的内容(字符串类型)
4. 立即执行的异步函数(核心逻辑区)
js
void async function () {
// Write your code here 👉 你的核心代码写在这里
// 直接输出需要的字符串,不需要处理输入 console.log("Hello Nowcoder!");
}()
这是整个代码的执行入口,也就是你需要编写核心逻辑的地方,我们拆解一下:
void async function (){...}()
:这是一个立即执行的异步函数表达式 (IIFE)。async
标记:允许函数内部使用await关键字(因为读取输入输出是异步操作)。void
:避免函数执行后返回值可能导致的语法问题,单纯让函数执行。- 最后的
()
:表示定义后立即执行这个函数。
核心代码写在哪里?
答案是:写在void async function () { ... }
这个函数内部(也就是注释// Write your code here
的位置)。根据题目的输入格式不同,你需要修改这个区域的代码。具体常见的输入格式见我第二部分详细讲解。
总结
这个模板的作用是标准化输入读取流程:
- 准备好读取输入的工具(
rl
接口,iter
迭代器,readline
函数)。 - 在立即执行的异步函数中,通过
await readline()
获取输入。 - 在函数内部编写你的核心逻辑(处理输入、计算、输出结果)。
二、常见出题形式
1.单组A+B
描述
给定两个整数a
和b
,请你求出a + b
的值。
输入描述:
第一行有两个整数a
和b
输出描述:
输入一个整数,代表a + b
的值。
示例
js
输入:
1 2
输出:
3
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// Write your code here
while(line = await readline()){
let tokens = line.split(' ');
let a = parseInt(tokens[0]);
let b = parseInt(tokens[1]);
console.log(a + b);
}
}()
核心逻辑解析
函数内部的while
循环:
js
while(line = await readline()) { ... }
- 作用:持续读取每一行输入 ,直到没有更多输入(
readline()
返回undefined
,循环终止)。 line = await readline()
:先调用readline()
读取每一行输入,赋值给line
。- 当没有输入时,
readline()
先返回undefined
,循环条件为false
,退出循环。
循环内部的代码
js
let tokens = line.split(' '); // 将一行输入按空格分割成数组(比如输入"1 2",得到["1", "2"])
let a = parseInt(tokens[0]); // 将第一个元素转为整数
let b = parseInt(tokens[1]); // 将第二个元素转为整数 console.log(a + b); // 输出结果
2.多组_A+B_EOF形式
描述
给定若干组测试数据,读取至文件末尾为止,每组数据有两个整数a和b,请你求出a + b的值。
输入描述
每行有两个整数a和b,读取至文件末尾为止
输出描述
输出若干行,每行一个整数,代表a + b的值。
示例
js
输入:
1 2
114 514
2024 727
输出:
3
628
2751
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 循环读取每一行输入,直到没有更多输入(EOF)
while(line = await readline()){
// 将一行输入按空格分割成数组(例如"1 2"分割为["1", "2"])
let tokens = line.split(' ');
// 将分割后的字符串转为整数
let a = parseInt(tokens[0]);
let b = parseInt(tokens[1]);
// 输出两数之和
console.log(a + b);
}
}()
3.多组_A+B_T组形式
描述
给定t组测试数据。每组数据有两个整数a和b,请你求出a + b的值。
输入描述
第一行有一个整数t,每行有两个整数a和b
输出描述
输出t行,每行一个整数,代表a + b的值。
示例
js
输入:
3
1 2
114 514
2024 727
输出:
3
628
2751
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 第一步:读取第一行,获取测试用例数量T
let T = parseInt(await readline());
// 第二步:循环T次,处理每组数据
for (let i = 0; i < T; i++) {
// 读取一行输入
let line = await readline();
// 分割成两个数字
let tokens = line.split(' ');
let a = parseInt(tokens[0]);
let b = parseInt(tokens[1]);
// 输出结果
console.log(a + b);
}
}()
4.多组_A+B_零尾模式
描述
给定若干组测试数据,最后一组数据为0 0,作为输入的结尾。每组数据有两个整数a和b,请你求出a + b的值。
输入描述
每行有两个整数a和b,最后一组数据为0 0,作为输入的结尾。
输出描述
输出若干行,每行一个整数,代表a + b的值。
示例
js
输入:
1 2
114 514
2024 727
0 0
输出:
3
628
2751
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 循环读取每一行输入
while(line = await readline()){
// 分割并转换为数字
let tokens = line.split(' ');
let a = parseInt(tokens[0]);
let b = parseInt(tokens[1]);
// 关键:判断是否为0 0,是则终止循环
if (a === 0 && b === 0) {
break; // 退出循环,不再处理后续输入
}
// 不是终止条件则输出结果
console.log(a + b);
}
}()
5.单组_一维数组
示例
js
输入:
3
1 4 7
输出:
12
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 第一步:读取第一行,获取数字的个数n
let n = parseInt(await readline());
// 第二步:读取第二行,获取包含n个数字的字符串
let line = await readline();
// 第三步:对字符串进行处理,转化为数字数组
let nums = line.split(" ").filter(x => x).map(Number); // 用空格分割,过滤空值
// 第四步:计算数组中所有数字的总和
let sum = nums.reduce((acc, curr) => acc + curr, 0);
// 第五步:输出总和
console.log(sum);
}()
6.多组_一维数组_T组形式
示例
js
输入:
3
3
1 4 7
1
1000
2
1 2
输出:
12
1000
3
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
// 读取测试用例总数T
const T = parseInt(await readline());
// 循环处理每组数据
for (let i = 0; i < T; i++) {
// 读取当前组的元素个数n
const n = parseInt(await readline());
// 读取当前组的数组元素行
const arrayLine = await readline();
// 将字符串分割为数字数组
const numbers = arrayLine.split(" ").filter(x => x).map(Number);
// 计算数组总和(使用reduce累加,初始值为0)
const sum = numbers.reduce((acc, current) => acc + current, 0);
// 输出当前组的总和
console.log(sum);
}
})();
7.单组_二维数组
示例
js
输入:
3 4
1 2 3 4
5 6 7 8
9 10 11 12
输出:
78
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 1. 读取第一行,获取二维数组的行数m和列数n
let firstLine = await readline();
let [m, n] = firstLine.split(' ').map(Number); // m=3, n=4(对应示例输入)
let totalSum = 0; // 存储总和
// 2. 循环读取m行数据(二维数组的每一行)
for (let i = 0; i < m; i++) {
let row = await readline(); // 读取一行数据(如"1 2 3 4")
let nums = row.split(' ').filter(x => x).map(Number); // 转为数字数组(如[1,2,3,4])
// 3. 累加当前行的所有元素到总和
let rowSum = nums.reduce((acc, curr) => acc + curr, 0);
totalSum += rowSum;
}
// 4. 输出二维数组所有元素的总和
console.log(totalSum);
}()
8.多组_二维数组_T组形式
示例
js
输入:
3
3 4
1 2 3 4
5 6 7 8
9 10 11 12
1 1
2024
3 2
1 1
4 5
1 4
输出:
78
2024
16
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
// 1. 读取测试用例总数T
const T = parseInt(await readline());
// 2. 循环处理每组二维数组
for (let t = 0; t < T; t++) {
// 2.1 读取当前组的行数m和列数n
const [m, n] = (await readline()).split(" ").filter(x => x).map(Number);
let totalSum = 0; // 存储当前组的总和
// 2.2 读取m行数据(二维数组的每一行)
for (let i = 0; i < m; i++) {
const row = (await readline()).split(" ").filter(x => x).map(Number);
// 累加当前行的所有元素
const rowSum = row.reduce((acc, curr) => acc + curr, 0);
totalSum += rowSum;
}
// 2.3 输出当前组的总和
console.log(totalSum);
}
})();
9.单组_字符串
描述
给定一个长度为n
的字符串s
,请你将其倒置,然后输出。
输入描述
第一行有一个整数n
,第二行有一个字符串s
,仅包含小写英文字符。
输出描述
输出一个字符串,代表倒置后的字符串s
。
示例
js
输入:
5
abcde
输出:
edcba
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 1. 读取第一行:字符串的长度n(本题中可忽略具体值,仅用于匹配输入格式)
const n = parseInt(await readline());
// 2. 读取第二行:需要反转的字符串
const str = await readline();
// 3. 反转字符串:
// - split('') 将字符串转为字符数组(如"abcde" → ['a','b','c','d','e'])
// - reverse() 反转数组(→ ['e','d','c','b','a'])
// - join('') 将数组转回字符串(→ "edcba")
const reversedStr = str.split('').reverse().join('');
// 4. 输出反转后的字符串
console.log(reversedStr);
}()
10.多组_字符串_T组形式
描述
给定t
组询问,每次只给出一个长度为n
的字符串s
,请你将其倒置,然后输出。
输入描述
第一行有一个整数t
,随后t
组数据。每组的第一行有一个整数n
,每组的第二行有一个字符串s
,仅包含小写英文字符。
输出描述
输出t
行,每行一个字符串,代表倒置后的字符串s
。
示例
js
输入:
3
5
abcde
8
redocwon
9
tfarcenim
输出:
edcba
nowcoder
minecraft
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
// 1. 读取测试用例总数T
const T = parseInt(await readline());
// 2. 循环处理每组字符串
for (let t = 0; t < T; t++) {
// 2.1 读取当前组的字符串长度n(仅用于匹配输入格式,反转逻辑不依赖此值)
const n = parseInt(await readline());
// 2.2 读取当前组需要反转的字符串
const str = await readline();
// 2.3 反转字符串:拆分为字符数组 → 反转数组 → 拼接为字符串
const reversedStr = str.split("").reverse().join("");
// 2.4 输出反转后的字符串
console.log(reversedStr);
}
})();
11.单组_二维字符数组
输入描述
第一行有两个整数n
和m
,随后n
行,每行有m
个字符,仅包含小写英文字符。
输出描述
输出一个二维字符数组。
示例
js
输入:
3 4
abcd
efgh
ijkl
输出:
lkji
hgfe
dcba
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
// 1. 读取第一行,获取二维数组的行数m和列数n
const [m, n] = (await readline()).split(" ").filter(x => x).map(Number);
// 2. 读取m行字符串,存储到数组中
const rows = [];
for (let i = 0; i < m; i++) {
rows.push(await readline());
}
// 3. 处理逻辑:
// a. 先将每行字符串反转(如"abcd" → "dcba")
// b. 再将所有行的顺序反转(如[行1, 行2, 行3] → [行3, 行2, 行1])
const reversedRows = rows
.map((row) => row.split("").reverse().join("")) // 每行字符反转
.reverse(); // 行顺序反转
// 4. 逐行输出处理后的结果
reversedRows.forEach((row) => console.log(row));
})();
12.多组_带空格的字符串_T组形式
描述
给定t
组询问,每次给出一个长度为n
的带空格的字符串s
,请你去掉空格之后,将其倒置,然后输出。
输入描述
第一行有一个整数t
,随后有t
组数据。每组的第一行有一个整数n
,每组的第二行有一个字符串s
,仅包含小写英文字符和空格,保证字符串首尾都不是空格。
输出描述
输出t行,每行一个字符串,代表倒置后的字符串s
。
示例
js
输入:
3
9
one space
11
two spaces
14
three spaces
输出:
ecapseno
secapsowt
secapseerht
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
// 1. 读取测试用例总数T
const T = parseInt(await readline());
// 2. 循环处理每组字符串
for (let t = 0; t < T; t++) {
// 2.1 读取当前组的字符串总长度n(用于匹配输入格式)
const n = parseInt(await readline());
// 2.2 读取带空格的字符串
const str = await readline();
// 2.3 处理逻辑:
// a. 先将字符串所有字符(包括空格)反转
// b. 再去除反转后字符串中的所有空格
const processed = str
.split("") // 拆分为字符数组(含空格)
.reverse() // 反转所有字符(包括空格)
.join("") // 拼接回字符串
.replace(/\s+/g, ""); // 去除所有空格(\s+匹配任意空白字符)
// 2.4 输出处理结果
console.log(processed);
}
})();
13.单组_保留小数位数
描述
给定一个小数 n ,请你保留 3 位小数后输出。
如果原来的小数位数少于 3 ,需要补充 0 。
如果原来的小数位数多于 3 ,需要四舍五入到 3 位。
输出描述
输出一个小数,保留 3 位。
示例
js
输入:
1.23
输出:
1.230
输入:
114.514
输出:
114.514
输入:
123
输出:
123.000
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 1. 读取输入的小数(单组输入,只需读一次)
const numStr = await readline();
// 2. 将字符串转换为浮点数
const num = parseFloat(numStr);
// 3. 保留3位小数:toFixed(3)会自动补零,确保结果是3位小数
const result = num.toFixed(3);
// 4. 输出格式化后的结果
console.log(result);
}()
14.单组_补充前导零
描述
给定一个正整数 n ,请你保留 9 个数位,然后输出。
如果数位少于 9 个,那么需要补充前导零。
输出描述
输出一个小数,保留 3 位。
示例
js
输入:
123
输出:
000000123
输入:
123456789
输出:
123456789
js
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function () {
// 1. 读取输入的数字(单组输入,读取一行即可)
const numStr = await readline();
// 2. 补充前导零至9位:
// - padStart(9, '0') 表示如果字符串长度不足9位,在前面补'0'直到长度为9
const result = numStr.padStart(9, '0');
// 3. 输出处理后的结果
console.log(result);
}()
三、总结
以上便是我们在笔试中经常会遇到的ACM输入输出题型,我们只需要特别牢记以下几点就可以很好得应对。
line = await readline()
:要注意await readline()获取的是一段字符串,后面我们还要自己将它分割或者转化为其他数据类型。let tokens = line.split(' ')
:这段代码作用是,将一行输入按空格分割成数组(例如"1 2"分割为["1", "2"])。let a = parseInt(tokens[0])
:这段代码的作用是,将分割的字符转化为数字。
掌握以上这些,再配合while
和for
语句就可以应对各种题型了。