Python 竞赛输入输出与字符串处理

2026-09-07 00:00    #Python   #算法竞赛  

暴力验证函数写好以后,还要把输入转换成列表、整数和字符串,再把答案按题目格式输出。Python 不需要复杂的扫描器;掌握 inputsplitmap 和解包,已经能处理大多数小数据。

读取一行

input() 读取一行文本,并自动去掉末尾换行:

1from io import StringIO
2
3source = StringIO("5\n")
4n = int(source.readline())
5
6assert n == 5

真实程序中可以直接写:

1# n = int(input())

这里注释掉调用,是为了让代码片段不等待标准输入。int 把字符串转换为整数,float 转换为浮点数。

一行读取多个整数

输入:

13 7 11 15

常见写法是:

1line = "3 7 11 15"
2a = list(map(int, line.split()))
3
4assert a == [3, 7, 11, 15]

执行过程可以拆开理解:

  1. line.split() 按任意空白切分,得到字符串列表;
  2. map(int, ...) 对每个字符串调用 int
  3. list(...) 保存转换后的全部整数。

如果元素个数固定,可以直接解包:

1line = "10 20"
2left, right = map(int, line.split())
3
4assert left == 10
5assert right == 20

真实竞赛代码通常写成:

1# a = list(map(int, input().split()))
2# x, y = map(int, input().split())

inputsys.stdin.buffer

input() 简单清楚,写小数据验证程序时通常足够。如果正式 Python 提交需要读取大量数据,可以使用缓冲输入:

 1from io import BytesIO
 2
 3source = BytesIO(b"4\n1 2 3 4\n")
 4readline = source.readline
 5
 6n = int(readline())
 7a = list(map(int, readline().split()))
 8
 9assert n == 4
10assert a == [1, 2, 3, 4]

真实程序把前两行替换成:

1# import sys
2# readline = sys.stdin.buffer.readline

bytes.split() 同样按空白切分,int(b"123") 也能直接得到整数,所以纯数字输入不必手动解码。

选择原则

验证代码优先使用容易读懂的 input()。只有输入确实很大时才换成 sys.stdin.buffer,不要让快读模板掩盖验证逻辑。

按 token 读取整份输入

当换行位置不重要时,可以把整份输入按空白切成 token:

 1def parse_case(data):
 2    tokens = iter(data.split())
 3    n = int(next(tokens))
 4    a = [int(next(tokens)) for _ in range(n)]
 5    target = int(next(tokens))
 6    return a, target
 7
 8
 9a, target = parse_case("4\n2 7 11 15\n9\n")
10
11assert a == [2, 7, 11, 15]
12assert target == 9

真实程序可以从 sys.stdin.buffer.read() 获得整份字节数据:

1def parse_all(data):
2    values = list(map(int, data.split()))
3    return values
4
5
6assert parse_all(b"1 2\n3 4\n") == [1, 2, 3, 4]

这种方式很快,但失去了行结构。题目包含整行字符串或空行有特殊含义时,应按行读取。

多组测试数据

常见格式是第一行给出测试组数 t

 1def parse_cases(text):
 2    lines = iter(text.strip().splitlines())
 3    test_count = int(next(lines))
 4    cases = []
 5
 6    for _ in range(test_count):
 7        n = int(next(lines))
 8        a = list(map(int, next(lines).split()))
 9        assert len(a) == n
10        cases.append(a)
11
12    return cases
13
14
15text = """\
162
173
181 2 3
194
205 5 8 13
21"""
22
23assert parse_cases(text) == [[1, 2, 3], [5, 5, 8, 13]]

把解析写成独立函数有一个好处:可以直接用字符串构造边界用例,不必创建临时输入文件。

输出列表和多行答案

print(*a) 使用 * 解包列表,相当于把元素作为多个参数传给 print

1from io import StringIO
2
3output = StringIO()
4a = [1, 2, 3]
5
6print(*a, file=output)
7assert output.getvalue() == "1 2 3\n"

sep 控制参数之间的分隔符,end 控制结尾:

1from io import StringIO
2
3output = StringIO()
4
5print(1, 2, 3, sep=",", end="!", file=output)
6assert output.getvalue() == "1,2,3!"

需要积累很多行时,先构造字符串列表,最后一次输出:

1answers = [3, 5, 8]
2text = "\n".join(map(str, answers))
3
4assert text == "3\n5\n8"

常用字符串操作

字符串支持下标和切片,但创建后不能原地修改:

1s = "algorithm"
2
3assert s[0] == "a"
4assert s[-1] == "m"
5assert s[1:4] == "lgo"
6assert s[::-1] == "mhtirogla"

常用判断与变换:

1s = "codeforces-2026"
2
3assert s.startswith("code")
4assert s.endswith("2026")
5assert s.replace("2026", "2027") == "codeforces-2027"
6assert "12345".isdigit()
7assert not "12a45".isdigit()
8assert " Ab C ".strip().lower() == "ab c"

字符串和字符列表之间经常互相转换:

1s = "cba"
2chars = list(s)
3chars.sort()
4result = "".join(chars)
5
6assert result == "abc"

一个完整的验证程序接口

把解析、暴力计算和格式化分开,测试会更简单:

 1def two_sum_exists(a, target):
 2    for i in range(len(a)):
 3        for j in range(i + 1, len(a)):
 4            if a[i] + a[j] == target:
 5                return True
 6    return False
 7
 8
 9def solve(text):
10    tokens = iter(text.split())
11    n = int(next(tokens))
12    a = [int(next(tokens)) for _ in range(n)]
13    target = int(next(tokens))
14    return "YES" if two_sum_exists(a, target) else "NO"
15
16
17assert solve("4\n2 7 11 15\n9\n") == "YES"
18assert solve("3\n1 2 3\n10\n") == "NO"

这种结构比在算法内部反复调用 input 更容易复用,也更容易为边界数据写断言。

比赛时需要根据题面格式快速查找可复制代码,见Python OJ 输入输出速查。更多暴力枚举方法见用 Python 快速编写算法暴力验证程序

参考资料