LoginSignup
6
5

More than 3 years have passed since last update.

SwiftでAtCoder攻略するノウハウを色々書いていく記事

Last updated at Posted at 2020-02-11

半年ぐらい離れていたんですが、最近またAtCoderをやっています。
Swift使いとしては、AtCoderのSwift2縛りがキツいんですが、ぼちぼちコンパイラアップデートしてくれそうです。

競プロ真剣にやるんなら、C++が圧倒的有利なんですが、
今の僕の状況的にC++まで手出せないので、Swiftで戦います。

この前気付いたんですが、AtCoderにSwiftで参戦している人口はめちゃくちゃ少ないです。

この記事はアップデートしていく自分用ノートみたいにしたいかなと思っています。
Swift5->swift2への逆引き(?)みたいな記事ってないので、案外困るんですよね。。。

やっていく中で詰まったら順次追記していきます。
後AtCoder側がSwift5にアップデートされたらこの記事自体要らなくなると思います。

テンプレート集

swift2です。

標準入力
import Foundation

/*
// 1
let N = Int(readLine()!)!

// 1 2
let split = readLine()!.componentsSeparatedByString(" ")
let N = Int(split[0])!
let S = Int(split[1])!

// 1 2 3 4 5
let array = readLine()!.componentsSeparatedByString(" ").map { Int($0)! }
*/
便利extension
extension Array where Element: Equatable {
    mutating func remove(value: Element) {
        if let i = self.indexOf(value) {
            self.removeAtIndex(i)
        }
    }
}

パクリ元:swiftで配列から値を指定して削除する

sort
var array = [1, 2, 3, 4, 5]
array = array.sort { $0 > $1 } // 降順
文字列のcount
let S = "aaaaa"
print(S.characters.count) //5
配列の最大値/最小値
array.maxElement()!
array.minElement()!
出力
// デフォルトのセパレーターはブランクだが、変えたい時
print("2020", "2", "22", separator: "-") //2020-2-22

2020/02/16追記

これ解くために辞書型使ったら色々ハマりました。
このC問題解けなかったのかなり屈辱だったので、辞書型について調べました。

  • Swift2系で辞書型で高階関数使うと戻り値がタプルの配列(Swift4.0から辞書型を返すようになった模様)
  • 辞書の要素にkey/valueでアクセスできない
    • これ地味にキツイです。.0/.1ならイケるんですが……
  • タプルも同様   swift: let tupleArray = dictionary.filter { $0.1 == max } //$0.valueだとコンパイルエラー let answer = tupleArray.map { $0.0 }.sort() answer.map { print($0) }
タプルの扱いがしんどい
typealias tuple = (key: String, value: Int)

こうするのが一番いいかなあ……
タプルは用法用量を守って正しくお使いください

Keyがなかったら1、あったらインクリメント、みたいな処理
let value = dictionary[key] ?? 0
dictionary[key] = value + 1

dictionary["Potato", default: 4]みたいな書き方はできません。

ABC155のC問題解けなかったのが悔しくて、死ぬほど再挑戦したんですが、どうもボトルネックが文字列のSortにあるところまで突き止めました。

import Foundation

let N = Int(readLine()!)!
var array = [String]()
var dictionary = [String: Int]()
var max = 0
for i in 0..<N {
  var key = readLine()!
  let value = dictionary[key] ?? 0
  dictionary[key] = value + 1
  if max < value + 1 {
    max = value + 1
  }
}
let answer = dictionary.filter { $0.1 == max }
array = answer.map { $0.0 }.sort() //ここのSortがボトルネック
array.forEach { print($0) }

ただSwiftの仕様上、辞書型は出力の順序が保証されないので、Sortは必須なんですが、
出力する単語が多いようなテストケース(おそらく)のときは、Sortで時間食ってどうしてもTLEになっちゃいます。
辞書じゃなくて、自前でStruct作るとか、配列でやってみるとか、KeyValuePairを検討するとか色々考えたんですが、どうしてもACせず。。。

C++とSwiftの速度比較 〜ソートアルゴリズム編〜

この記事を見ると、50万件の文字列(3〜10文字のランダムなアルファベット)のソートでSwiftはC++なら0.5秒で終わるSortに6.3秒かかっています。
6.3秒かかっているSortはブログ記事投稿した方の自作Sort関数で、ライブラリ標準Sortだと3.1秒ですが、正直これでも遅い気はします。

文字列のSortが絡むとSwiftだとACできなんじゃないか? と今回感じました。
上記のコード見て、何か計算量削る方法思いつく方いたら是非ご教示ください……僕はもう諦めます……

2020/03/01

累乗したかったんですが、pow()が使えなくて困りました。
「error: use of unresolved identifier 'pow'」って言われます。
Foundationは入れてたし、インプットもDouble型にしてみたり、試せることは試したんですが、ダメでした。

結局こんな書き方しました。涙ぐましいですね。

// 1 <= N <= 3
// var answer = pow(10, N - 1) がやりたい
var answer = 1
for _ in 1..<N { answer = answer * 10 }

そんなに困んないっちゃ困んないんですが、バグが潜む箇所が増えちゃうんで、pow()使いたかったんですが、どうしても動かないんですよね〜

func pow(_ number: Int, multiplier: Int) -> Int {
  var answer = 1
  for _ in 0 ..< multiplier {
    answer *= number 
  }
  return answer
}

powが動かないのまあまあ困るので、やっぱ書きました。
指数部にマイナスが来るケースは想定していないので、必要ならマイナスだったら割り算するみたいなロジックを入れたら良いかと。

エディタ問題

playgroundだと標準入力が受け取れなくて、
readline部分を書き換えて、変数で受け取っちゃうとか、やりようはあるんですが、まあちょっとめんどくさいですよね。

最近気付いたんですが、コマンドラインツールとしてプロジェクト作成すれば、
標準入力を受け取るプログラムとしてSwiftが書けて、入力補完が効くので嬉しいので、これでやるのが一番いいですね。
AtCoder側がSwift5に対応してくれた後なら。

一昨日これで参戦したら、やっぱいちいちSwift2向けに書き換えるオーバーヘッドがしんどくて、
結局いつの間にかAtCoder上のコードテストでデバッグしている僕がいました。

現状ブラウザで書いちゃうのが一番いいかなあという感じです。

関連記事

AtCoderにSwiftで挑戦するときの標準入出力

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5