B-Teck!

お仕事からゲームまで幅広く

【Kotlin】Kotlinで競プロする

PaizaやってたときはPHP+JS、AtCoderでは主にJavaで解いてたんですが、
ここ半年の案件でずっとKotlinを書いていたらJavaが書けなくなってしまいました。
なので、KotlinでAtCoderを解く準備をするなどしています。

注意点として、AtCoderのKotlinは記事執筆時点で1.0.0なので、新し目の機能が使えなかったりしますが…。

そんな感じで、Kotlinで標準入出力を行う方法などをご紹介します。

Scannerとprintln()で入出力する

一番基本的な入出力のとり方です。
Kotlinで書くとこうなります。

import java.util.Scanner
fun main(args: Array<String>) {
    // Scannerを使う
    val sc = Scanner(System.`in`)
    // 文字列で取得
    println(sc.next())
    // 数値で取得
    println(sc.nextInt())
}

ただ、Scannerは結構遅いので、ある程度問題解いてくると不満が出てくると思います。
ので、自分は次に書くような形で書いています。

readLine()とPrintWriterで入出力する

Java競技プログラミングメモ - Qiita
この記事にもあるようにScannerはあまり早くないです。
また、都度println()を行うことでIOに割かれる時間が増えて、TLEの原因になります。

なので、内部的にBufferedReaderを使用しているreadLine()を使って行ごとに入力を読み込み、
PrintWriterで最後にまとめて出力をflush()する形をとっています。

import java.io.PrintWriter

fun main(args : Array<String>) {
    // PrintWriterを使う
    val pw = PrintWriter(System.out)
    // 文字列で取得
    pw.println(readLine())
    // 数値で取得
    pw.println(readLine()!!.toInt())
    // まとめて出力
    pw.flush()
}

その他Kotlinで書いて嬉しいところ

あと、Kotlinでは分解宣言が使えるので、1行入力の複数の値を一発で取るのがすごい楽です。

import java.io.PrintWriter

fun main(args : Array<String>) {
    val pw = PrintWriter(System.out)
    // A, B, C の形で入力された文字列を一発で各変数に入れる
    val (a, b, c) = readLine()!!.split(" ")
    pw.println(a)
    pw.println(b)
    pw.println(c)
    // X, Y, Z の形で入力された数字を一発で各変数に入れる
    val (x, y, z) = readLine()!!.split(" ").map{ it.toInt() }
    pw.println(x)
    pw.println(y)
    pw.println(z)
    pw.flush()
}

Collectionの取り回しもよく、ListとかMapを取り回すような問題では見通しよく書けるという利点もありました。

普段使ってる解答用テンプレ

これらを踏まえて、こういうテンプレを使って問題を解いています。
ちょっとKotlinで解いてみようかな?ってときに参考になればと思います。

import java.io.PrintWriter
val pw = PrintWriter(System.out)
fun main(args : Array<String>) {
    func()
    pw.flush()
}

fun func() {
    // 回答をここに書く
}

// 入力取得
fun next() = readLine()!!
fun nextInt() = next().toInt()
fun nextLong() = next().toLong()
fun nextDouble() = next().toDouble()
fun listOfString() = next().split(" ")
fun listOfInt() =listOfString().map { it.toInt() }
fun listOfLong() =listOfString().map { it.toLong() }
fun listOfDouble() =listOfString().map { it.toDouble() }

// 約数のList
fun divisor(value : Long) : List<Long> {
    val max = Math.sqrt(value.toDouble()).toLong()
    return (1..max)
        .filter { value % it == 0L }
        .map { listOf(it, value / it) }
        .flatten()
        .sorted()
}

// 範囲内の素数を取得
// fromだけ指定すると戻り値の個数で素数判定ができる
fun prime(from : Long, to : Long = from) : List<Long> {
    return (from..to).filter { i ->
        val max = Math.sqrt(i.toDouble()).toLong()
        (2..max).all { j ->  i % j != 0L }
    }
}

// 素因数分解
fun decom(value : Long) : List<Long>{
    if (value == 1L) return listOf(1)
    val max = Math.sqrt(value.toDouble()).toLong()
    return prime(2, max).filter { value % it == 0L }
}

// 最大公約数
fun gcd(a : Long, b : Long) : Long {
    return if (a % b == 0L) b else gcd(b, a % b)
}

// 文字列を入れ替え
fun swap(base : String, a : String, b : String) : String {
    return base.map {
        when (it) {
            a.toCharArray()[0] -> b
            b.toCharArray()[0] -> a
            else -> it.toString()
        }
    }.joinToString()
}

fun println(value : Any) {
    pw.println(value)
}