AtCoder 入力の高速受け取り@TypeScript
そういうクラスを書く
スペースと改行を区別せずに空白文字とし、空白文字で区切られた各区間を「トークン」とします。
new InputScanner(inputText)から.str(), .int(), .num(), .bigint()でトークンを1つ読むことができます。
最後まで読み終わるとundefinedを返すので、非nullアサーション!をつけて型エラーを回避する必要あり
トークン読むメソッドの引数に数値nを渡すと戻り値が配列になって一気にn個読めます。
code:scanner.ts
/**
* 入力をスキャンして、トークンを順に取得するためのクラスです。
*/
class InputScanner {
/** 新しい入力スキャナーインスタンスを生成します。 */
constructor(str: string) {
this.#str = str;
this.#len = str.length;
this.#idx = 0;
}
/** ある文字がトークンの区切りとみなせる文字(' '・'\n'・'\r'・'\t')であるかを判定します。 */
return c === 32 || c === 10 || c === 13 || c === 9;
}
while (this.#idx < this.#len) {
const c = this.#str.charCodeAt(this.#idx);
if (!this.#isSpace(c)) break;
this.#idx++;
}
}
/**
* 次のトークンをstringで取得します。
* - 引数なしで呼んだ場合、戻り値は1つのstringです。
* - もうトークンが存在しない場合、undefinedを返します。
* - 引数(n: number)ありで呼んだ場合、戻り値は最大で長さnのstring[]です。
* - トークンがn個以上残っていなかった場合、長さn未満のstring[]が返されます。
* - 残りトークンが0個の状態で引数ありで呼んだ場合は、空の配列が返されます。
*/
str(): string | undefined;
str(n: number): string[];
str(n?: number): string | string[] | undefined {
if (n == null) {
// 1つだけトークンを読む場合
this.#skipSpaces();
if (this.#idx >= this.#len) return undefined;
const startIdx = this.#idx;
while (this.#idx < this.#len) {
const c = this.#str.charCodeAt(this.#idx);
if (this.#isSpace(c)) break;
this.#idx++;
}
return this.#str.substring(startIdx, this.#idx);
} else {
// 複数トークンを読む場合 (str()を内部で呼ぶ)
const result: string[] = [];
for (let i = 0; i < n; i++) {
const token = this.str();
if (token == null) break;
result.push(token);
}
return result;
}
}
/**
* 次のトークンを(浮動小数点数で表せる)数値とみなし、numberで取得します。
* - 引数なしで呼んだ場合、戻り値は1つのnumberです。
* - もうトークンが存在しない場合、undefinedを返します。
* - 引数(n: number)ありで呼んだ場合、戻り値は最大で長さnのnumber[]です。
* - トークンがn個以上残っていなかった場合、長さn未満のnumber[]が返されます。
* - 残りトークンが0個の状態で引数ありで呼んだ場合は、空の配列が返されます。
*/
num(): number | undefined;
num(n: number): number[];
num(n?: number): number | number[] | undefined {
if (n == null) {
// 1つだけトークンを読む場合 (str()を内部で呼ぶ)
const token = this.str();
if (token == null) return undefined;
return Number.parseFloat(token);
} else {
// 複数トークンを読む場合 (number()を内部で呼ぶ)
const result: number[] = [];
for (let i = 0; i < n; i++) {
const token = this.num();
if (token == null) break;
result.push(token);
}
return result;
}
}
/**
* 次のトークンを(浮動小数点数で表せる)整数とみなし、numberで取得します。
* - 引数なしで呼んだ場合、戻り値は1つのnumberです。
* - もうトークンが存在しない場合、undefinedを返します。
* - 引数(n: number)ありで呼んだ場合、戻り値は最大で長さnのnumber[]です。
* - トークンがn個以上残っていなかった場合、長さn未満のnumber[]が返されます。
* - 残りトークンが0個の状態で引数ありで呼んだ場合は、空の配列が返されます。
*/
int(): number | undefined;
int(n: number): number[];
int(n?: number): number | number[] | undefined {
if (n == null) {
// 1つだけトークンを読む場合 (str()を内部で呼ぶ)
const token = this.str();
if (token == null) return undefined;
return Number.parseInt(token);
} else {
// 複数トークンを読む場合 (number()を内部で呼ぶ)
const result: number[] = [];
for (let i = 0; i < n; i++) {
const token = this.int();
if (token == null) break;
result.push(token);
}
return result;
}
}
/**
* 次のトークンを(BigIntで表せる)整数とみなし、bigintで取得します。
* - 引数なしで呼んだ場合、戻り値は1つのbigintです。
* - もうトークンが存在しない場合、undefinedを返します。
* - 引数(n: number)ありで呼んだ場合、戻り値は最大で長さnのbigint[]です。
* - トークンがn個以上残っていなかった場合、長さn未満のbigint[]が返されます。
* - 残りトークンが0個の状態で引数ありで呼んだ場合は、空の配列が返されます。
*/
bigint(): bigint | undefined;
bigint(n: number): bigint[];
bigint(n?: number): bigint | bigint[] | undefined {
if (n == null) {
// 1つだけトークンを読む場合 (str()を内部で呼ぶ)
const token = this.str();
if (token == null) return undefined;
return BigInt(token);
} else {
// 複数トークンを読む場合 (number()を内部で呼ぶ)
const result: bigint[] = [];
for (let i = 0; i < n; i++) {
const token = this.bigint();
if (token == null) break;
result.push(token);
}
return result;
}
}
}