高速な入出力
読了目安 約2分
print の連打は TLE のもと。出力は配列に貯めて joined で 1 回にまとめる定石を身につける。
この章の目次
解法は正しいのに、時間切れ (TLE) で落ちる。 Swift の競プロでは、その原因が入出力の速度であることが珍しくありません。
print の呼びすぎが遅い
readLine() は 10^5 回呼んでも、通常は間に合います。
一方 print は、1 回ごとに出力処理の固定コストがかかります。
10^5 行を 1 行ずつ print すると、このコストが積み重なって TLE の原因になります。
まとめて出力する定石
出力を配列に貯めて、最後に joined(separator: "\n") で 1 回だけ print します。
var lines: [String] = []
for i in 1...5 {
lines.append(String(i * i))
}
print(lines.joined(separator: "\n"))行数がいくつになっても print は 1 回です。
出力の行数が多い問題では、最初からこの形で書いてください。
試してみよう: separator を " " に変えて、空白区切りの 1 行出力になることを確かめてください。
数値変換の速い形
split(separator: " ") が返すのは Substring の配列です。
Substring は元の文字列の一部を指すだけの軽い型で、Int(...) に直接渡せます。
// 遅い形: いったん String を作ってから変換する
let a = readLine()!.split(separator: " ").map { Int(String($0))! }
// 速い形: Substring のまま変換する
let b = readLine()!.split(separator: " ").map { Int($0)! }Int(String($0))! は、要素の数だけ文字列のコピーを作ります。
いつもの定型句 Int($0)! が、そのまま速い形です。
入力全体を一括で読む
FileHandle.standardInput.readDataToEndOfFile()という手段もあります。 書き方が難しいわりに、ABC の制約ではreadLine()との差が出にくいため、まずは不要です。
学びどころ
| 概念 | 一言まとめ |
|---|---|
readLine() | 10^5 回呼んでも通常は間に合う |
print の連打 | 1 回ごとの固定コストが積もって TLE のもと |
| まとめ出力 | 配列に貯めて joined(separator: "\n") で 1 回 |
Int($0)! | Substring のまま変換するのが速い形 |
演習
1 行目に整数 N が入力されます。 2 行目に N 個の整数 A_1 … A_N が空白区切りで入力されます。
各 A_i を 2 倍した値を、入力の順に N 行で出力してください。
制約: 1 ≤ N ≤ 2×10^5、0 ≤ A_i ≤ 10^9
採点データは小さめですが、本番の 2×10^5 行を想定して、まとめ出力の形で書いてください。
入力例:
3
1 2 3
出力例:
2
4
6模範解答
let n = Int(readLine()!)!
let a = readLine()!.split(separator: " ").map { Int($0)! }
var lines: [String] = []
lines.reserveCapacity(n)
for x in a {
lines.append(String(x * 2))
}
print(lines.joined(separator: "\n"))reserveCapacity は配列の領域を先に確保する関数で、なくても正解です。
N 行を 1 行ずつ print する書き方でも小さな採点データは通りますが、本番の 2×10^5 行では危険です。