入力について
普通は fmt.Scan
で問題ないのですが,高速な入力が要求される問題では fmt.Scan
だと遅すぎることがあります。fmt.Scan
では 3*106 個の整数の入力に10秒以上かかりました。
高速な入力のためには bufio
を使います。bufio
を使った方法では 3*106 個の整数の入力にかかる時間は0.1秒以下でした。bufio.Scanner
の Scan()
と Text()
の組み合わせで多くの場合は事足りるのですが,これにも少し問題があります。Scan()
で読み取れるトークンの最大長は MaxScanTokenSize
という定数で定義されており,Go 1.8 では 64*1024 でした。これでは 105 や 106 程度の長さの文字列の処理を要求する問題に対応することができません。Buffer([]byte, int)
でバッファの大きさを変えることができますが,バッファの長さを変えるのを忘れてWAになってしまいそうです。
そこで bufio.Reader
の ReadLine()
を使います。戻り値の isPrefix
を確認して ReadLine()
を複数回呼び出すことで長い文字列でも読み取ることができます。
出力について
os.Stdout
はバッファされないため,毎回flushしているのと同じような状態で遅いです。os.Stdout
を直接利用している fmt.Printf
なども同様です。これを解決するために bufio
を利用しています。プログラム終了前にはflushしてやる必要がありますが,忘れやすいのでmainの先頭で defer io.Flush()
としてやるほうが良いと思います。
配列の中身を空白区切りで出力したい場合が多々ありますが,...interface{}
の実引数として []int
や []string
を与えることはできません。そのため []int
と[]string
のためだけに配列の中身を空白区切りで出力する関数を定義しています。
[]int
や []string
から []interface{}
への変換についての話題が気になる方は以下のページを参照してください。
InterfaceSlice · golang/go Wiki · GitHub
その他
maxやminの関数はint用だけ特別に用意しています。Go言語にはジェネリクスが導入される予定がないので筋肉で解決しましょう。以下のリポジトリがすごくおもしろいです。
Go言語のPrintfのフォーマット指定子には,構造体の中身が見られる ‘%+v’ というものがあり,デバッグに便利です。printfデバッグするためにLog関数を定義しています。IntelliJ IDEAのデバッガが使いやすいのでprintfデバッグを使わないことも多いですが。
ソースコード
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) type Io struct { reader *bufio.Reader writer *bufio.Writer tokens []string nextToken int } func NewIo() *Io { return &Io{ reader: bufio.NewReader(os.Stdin), writer: bufio.NewWriter(os.Stdout), } } func (io *Io) Flush() { err := io.writer.Flush() if err != nil { panic(err) } } func (io *Io) NextLine() string { var buffer []byte for { line, isPrefix, err := io.reader.ReadLine() if err != nil { panic(err) } buffer = append(buffer, line...) if !isPrefix { break } } return string(buffer) } func (io *Io) Next() string { for io.nextToken >= len(io.tokens) { line := io.NextLine() io.tokens = strings.Fields(line) io.nextToken = 0 } r := io.tokens[io.nextToken] io.nextToken++ return r } func (io *Io) NextInt() int { i, err := strconv.Atoi(io.Next()) if err != nil { panic(err) } return i } func (io *Io) NextFloat() float64 { i, err := strconv.ParseFloat(io.Next(), 64) if err != nil { panic(err) } return i } func (io *Io) PrintLn(a ...interface{}) { fmt.Fprintln(io.writer, a...) } func (io *Io) Printf(format string, a ...interface{}) { fmt.Fprintf(io.writer, format, a...) } func (io *Io) PrintIntLn(a []int) { b := []interface{}{} for _, x := range a { b = append(b, x) } io.PrintLn(b...) } func (io *Io) PrintStringLn(a []string) { b := []interface{}{} for _, x := range a { b = append(b, x) } io.PrintLn(b...) } func Log(name string, value interface{}) { fmt.Fprintf(os.Stderr, "%s=%+v\n", name, value) } func intMin(a, b int) int { if a < b { return a } return b } func intMax(a, b int) int { if a > b { return a } return b } func main() { io := NewIo() defer io.Flush() A := io.NextInt() B := io.NextInt() C := io.NextInt() S := io.Next() io.PrintLn(A+B+C, S) }
偉大な先人の記事
おすすめの本
- 作者: 松木雅幸,mattn,藤原俊一郎,中島大一,牧大輔,鈴木健太,稲葉貴洋
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/09
- メディア: 大型本
- この商品を含むブログ (3件) を見る