Swiftでpaizaスキルチェック、問題集をやってます。
ググったものを残してます。
入力系
まとめて入力を受取る
let inputs = (0..<10000).map { _ in Int(readLine()!)! }
map()メソッドは、与えられた範囲内の整数を1つずつ順番に処理していくため、
1回のreadLine()で10000個の入力がまとめて処理されます。
そのため、1つずつ入力を受け取る場合と比較して、入力処理の速度が向上します。
ただし、まとめて入力を受け取る場合には、入力値の数が非常に大きい場合にはメモリに負荷がかかることがあります。
例えば、入力値が数百万、数千万以上になる場合は、まとめて入力を受け取る場合でもメモリ制限に達する可能性があります。
この場合は、バッファリングなどの方法を使用して、メモリ消費量を最小限に抑える必要があります。
var inputs = [Int]()
for _ in 0..<10000 {
let input = Int(readLine()!)!
inputs.append(input)
}
1つの入力に対して1回のreadLine()を呼び出しています。
つまり、10000回のループで10000回readLine()を呼び出していることになります。
この方法は入力が少ない場合には問題ありませんが、大量の入力を受け取る場合は遅くなるためまとめて受取る方法が良いです。
文字列系
複数の文字列を配列にバラす
let str="a a a a a a a"
print(str.split(separator: " ")) // ["a","a","a","a","a","a","a"]
let num="1 2 3 4 5"
print(num.split(separator: " ")) // ["1","2","3","4","5"]
print(num.split(separator: " ").map {Int($0)!}) // [1, 2, 3, 4, 5]
改行しないで出力
var i=0
for _ in 0..<1000 {
if i != 999 {
i+=1
print(i, terminator: " ") //'1 2 3 4 5 6... 997 998 999 '
} else {
print(i+1) //'1000\n'
}
}
文字列をスペースで区切る
//スペースで区切る
print("Samurai", "Engineer", "Blog", separator: " ") // Samurai Engineer Blog
let num = 100
print("num =", num, separator: " "); // num = 100
参考:print出力
複数入力を配列にして文字列で表示する
var arr = [String]() // 空の配列
for _ in 0..<10 {
let s = readLine()! // "1"..."9"
arr.append("\(s)") // 入力内容を配列に格納
}
print(arr) // ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
let str = arr.joined(separator: "|") // 配列要素を|でくっつける
print(str) // "0|1|2|3|4|5|6|7|8|9"
文字列を一文字ずつ配列にして分割する
let rr = "Qiita"
let room = Array(rr)
print(room) // ["Q","i","i","t","a"]
// ※Array(String)変換した配列要素は[Character型]であることに注意する
// var str = [String]()のような宣言をしても型違うエラーでる
// var str = [Character]()ならおk
参考:【swift】文字列を一文字ずつに分割する時のArray("hoge")の型について
空白をカンマ区切に置換する
import Foundation // 忘れないように
let input_line=readLine()! // "1 2"
print(input_line.replacingOccurrences(of: " ", with: ",")) // "1,2"
末尾文字の指定
import Foundation
let input_line=readLine()! // "1 2 3 4\n"
print("\(input_line.replacingOccurrences(of: " ", with: ",")),") // "1,2,3,4,"
print(input_line.replacingOccurrences(of: " ", with: ","), terminator: ",") // "1,2,3,4/"
文字列N個出力
let i=readLine()!.split(separator: " ") // "3 10 99"
let n=Int(i[0])! // 3
for s in 1...n {
if s < n {
print("(\(i[1]), \(i[2]))", terminator: ", ") // "(10, 99), "
} else {
print("(\(i[1]), \(i[2]))") // "(10, 99)"
}
}
// -> "(10, 99), (10, 99), (10, 99)"
文字列を連続で出力する
let i=3
let s="Qiita"
print(String(repeating: "\(s)", count: i)) // "QiitaQiitaQiita"
print(String(repeating: "\(s)\n", count: i))
// "Qiita"
// "Qiita"
// "Qiita"
文字の切り出し
let str="ABCDEFGH"
print(str.prefix(3)) // "ABC"
参考:【初学者向け】ややこしいSwiftの文字列切り出しの処理を理解する
指定indexに文字、文字列の挿入
var s="QiitaQiitaQiita"
if s.count > 10 { // もし10文字未満の時insertしようとするとクラッシュするので回避
s.insert(contentsOf: "\n", at: s.index(s.startIndex, offsetBy: 10))
// insert(contentsOf: 挿入文字列, at: 場所)
}
print(s) // QiitaQiita\nQiita
参考:Swiftで指定したindexに文字・文字列を挿入する方法
String配列の1文字目のみ抽出したい(略称)
let n=4
var s=["qiita", "swift", "kyopro", "xcode", "iosdc"]
for i in 0..<n {
if i==n-1 {
print(s[i].prefix(1))
} else {
print(s[i].prefix(1), terminator: "")
}
} //-> qskxi
文字列から指定位置の文字を取り出す方法
let str = "Qiita"
let index1 = str.index(str.startIndex, offsetBy: 3) //startIndex:0から3つ進む
//["Q","i","i","t","a"] Array(str)した際のindexをイメージ
//[ 0 1 2 3 4 ] offsetByで指定した数値はどこか確認
print(str[index1]) // t
print(index1)// "_rawBits: 65793" //printするとbit数?がでてくるInt()!は不可。
//index部分にしか使えなさそう
let str = "ABCDEF"
let index1 = str.index(str.endIndex, offsetBy: -2) //endIndex:0から2つ戻る
//["A","B","C","D","E","F"] Array(str)した際のindexをイメージ
//[ -6 -5 -4 -3 -2 -1] offsetByで指定した数値はどこか確認
print(str[index1]) // E
// 出力の書き方に注意する
文字列を逆順(反転)に並替え
let str="Qiita"
print(String(str.reversed())) // "atiiQ"
文字置換
import Foundation // 忘れない
let str="91ta91ta91ta"
print(str.replacingOccurrences(of: "91", with: "Qii")) // "QiitaQiitaQiita"
辞書置換(複数の文字置換)
import Foundation // 忘れない
var str="Abeshinzo"
var newStr: String {
// 辞書作成
let dictionary = ["A": "","I": "","U": "","E": "","O": "","a": "","i": "","u": "","e": "","o": ""]
return dictionary.reduce(str) { $0.replacingOccurrences(of: $1.key, with: $1.value) }
}
print(newStr) //"bshnz"
文字列検索
import Foundation // 忘れない
let i="a"
let str="Qiita"
var flg:Bool=str.contains(i) // ”Qiita”に"a"が含まれる場合True
if flg {
print("YES")
} else {
print("NO")
}
文字列から数値のみ抽出
let ns="Qiita910"
print(ns.filter("0123456789".contains)) // 919
特定の文字列が含まれているか(contains)
import Foundation // 忘れない
let s="Qiita Advent Calendar"
if s.contains("Qiita") {
// contains("抽出文字")があればtrue
print("Qiitaが含まれています")
} else {
print("Qiitaが含まれていません")
}
特定の文字を含まれていれば抽出する
import Foundation // 忘れない
let strs = [ "ab", "bc", "cd" ]
let results = strs.filter({ $0.contains("b") })
print( results ) // ["ab", "bc"]
参考:Swiftのmap, filter, reduce(などなど)はこんな時に使う!
参考:【Swift】Array の contains の挙動の整理
数値系
金額表示(カンマ区切り対応)
※長文非対応→Int型でエラーが出るため
func decimalStyle(priceValue: String) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = ","
formatter.groupingSize = 3
if let priceValue = Int(priceValue) {
return formatter.string(from: NSNumber(value: priceValue)) ?? "\(priceValue)"
}
return priceValue
}
let price = decimalStyle(priceValue: "3000000")
print(price) // 3,000,000
金額表示、長文lenが3の倍数時限定ゴリ押し出力
import Foundation
var result : [String] = [] // 空の配列
let str=readLine()! // "123456789"
for i in str {
result.append(String(i)) // 文字列を1字ずつの配列にバラす
}
for i in 0..<result.count { // 1文字ずつループ出力する
if i % 3 == 0 && i != 0 { // 3で割切れるかつ末尾index以外のとき","挿入かつ改行なし
print(",", terminator: "")
}
print(result[i], terminator: "") // 1文字改行なし
} // -> "123,456,789"
金額表示(解) 長文、len関係なく対応
import Foundation
let str="1234567"
var result : [String] = [] // 空の配列
var result2 : [String] = []
for i in str {
result.append(String(i)) // 文字列を配列化
}
print(result) // ["1", "2", "3", "4", "5", "6", "7"]
result.reverse()
print(result) // ["7", "6", "5", "4", "3", "2", "1"]
for i in 0..<result.count { // 1文字ずつループ出力する
if i % 3 == 0 && i != 0 { // 3で割切れるかつ末尾index以外のとき","挿入かつ改行なし
result2.append(",")
}
result2.append(result[i])
}
print(result2) // ["7", "6", "5", ",", "4", "3", "2", ",", "1"]
result2.reverse()
print(result2) // ["1", ",", "2", "3", "4", ",", "5", "6", "7"]
for i in 0..<result2.count { // 配列ループ出力する
print(result2[i], terminator: "") // 1文字改行なし
}
// -> [1,234,567]
九九の生成
for i in 1..<10 {
for j in 1..<10 {
if j == 9 {
print("\(i*j)")
} else {
print("\(i*j) " ,terminator: "" )
}
}
}
// ->
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
絶対値変換
let num = -1
print(abs(i)) // 1
mapで配列要素をキャストする
let n=["8", "10", "1"]
let s=n.map{Int($0)!}
print(s) //[8, 10, 1]
有効桁数表示
import Foundation // 忘れない
let i=3.14 // Double型
var n = String(format: "%.3f", i)
print(n) // "3.140"
print(String(format: "%.3f", i)) // "3.140"
import Foundation
let i=1080
print(i/100*100) // 1000
let s=Double(1080)
print(Int(floor(s/100)*100)) // 1000
ゼロサプレス
import Foundation // 忘れない
let i=readLine()!.split(separator: " ") // "4 3"
var n = Int(i[0])! // 4
var m = Int(i[1])! // 3
for _ in 0..<n {
let str=Int(readLine()!)!
print(String(format: "%\(m)d", str))
}
入力値
4 3 // 4回入力あり、3桁へゼロサプレス
0
8
81
813
結果
0
8
81
813
罫線入り九九
import Foundation // 忘れない
for i in 1..<10 {
for j in 1..<10 {
let m = i*j
let n = String(format: "%02d", m) // 2桁表示統一
if j == 9 {
if i==9 {
print("\(n)")
} else {
print("\(n)\n==========================================")
}
} else {
print("\(n)" ,terminator: " | " )
}
}
}
01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09
==========================================
02 | 04 | 06 | 08 | 10 | 12 | 14 | 16 | 18
==========================================
03 | 06 | 09 | 12 | 15 | 18 | 21 | 24 | 27
==========================================
04 | 08 | 12 | 16 | 20 | 24 | 28 | 32 | 36
==========================================
05 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45
==========================================
06 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54
==========================================
07 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63
==========================================
08 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72
==========================================
09 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81
ペア罫線表出力
let input_line=readLine()!.split(separator: " ") // "2 3 7 8" -->"h w a b"
let h = Int(input_line[0])!
let w = Int(input_line[1])!
let ab = "(\(input_line[2]), \(input_line[3]))" // "(7, 8)"
var num = (6*w)+(3*(w-1)) // 罫線は上部の文字数と等しくする
// 文字を連続出力
let s = String(repeating: "=", count: num)
for i2 in 1...h {
for i in 1...w {
if i == w {
print(ab)
if i2 == h {
} else {
print(s)
}
} else {
print(ab, terminator: " | ")
}
}
}
(7, 8) | (7, 8) | (7, 8)
========================
(7, 8) | (7, 8) | (7, 8)
累乗計算
import Foundation // 忘れない
let input_line=readLine()!.split(separator: " ") // ”5 3” -> ["5","3"]
let r1=Double(input_line[0])! // 5.0
let r2=Double(input_line[1])! // 3.0
let r3=Int(pow(r1,3)-pow(r2,3)) // pow(Double, 累乗数)
// 5.0^3 - 3.0^3 = 125.0 - 27.0 = Double:97.0 -> Int:97
print(r3) // 97
powはdouble型でしか使えないことに注意する。
小数第4位の四捨五入(まるめ)
import Foundation // 忘れない
let ans=String(format: "%.4f", 1.001999999) // "1.0020"
B電話の距離(リファクタリングしたい)
import Foundation // 忘れない
var str=Array["0120-44-4444"]
var total = 0
for i in 0..<str.count {
if str[i]=="-" {
// 何もしない
} else {
if str[i]=="0" {
total+=24
}
if str[i]=="1" {
total+=6
}
if str[i]=="2" {
total+=8
}
if str[i]=="3" {
total+=10
}
if str[i]=="4" {
total+=12
}
if str[i]=="5" {
total+=14
}
if str[i]=="6" {
total+=16
}
if str[i]=="7" {
total+=18
}
if str[i]=="8" {
total+=20
}
if str[i]=="9" {
total+=22
}
}
}
print(total) // 134
数値配列の追加、削除(キュー)
let q=Int(readLine()!)!
var str=[Int]()
for _ in 0..<q {
let q1=readLine()!.split(separator: " ")
if Int(q1[0])!==1 {
str+=[Int(q1[1])!]
for i in 0..<str.count {
if i==str.count-1 {
print(str[i])
} else {
print(str[i], terminator: " ")
}
}
} else {
// キューなのでindex[0]を消す
str.removeFirst()
if str==[] {
print()
} else {
for i in 0..<str.count {
if i==str.count-1 {
print(str[i])
} else {
print(str[i], terminator: " ")
}
}
}
}
}
数値配列の追加、削除(スタック)
let n=Int(readLine()!)!
var arr=[Int]()
for _ in 0..<n {
let m=readLine()!.split(separator: " ")
// 1のときスタック
if Int(m[0])!==1 {
arr+=[Int(m[1])!] //基本追加時は+=がいいかも、append()はString専用
// 配列中身を半角スペースで表示
for i in 0..<arr.count {
if i==arr.count-1 {
print(arr[i])
} else {
print(arr[i], terminator: " ")
}
}
} else {
// 2のときポップ
arr.removeLast() //最終要素を削除
// 配列中身がないときは改行のみ出力する
if arr == [] {
print()
} else {
// 配列中身を半角スペースで表示
for i in 0..<arr.count {
if i==arr.count-1 {
print(arr[i])
} else {
print(arr[i], terminator: " ")
}
}
}
}
}
参考:配列の要素の追加や削除について
参考:【Swift】配列操作まとめ
進数変換
let num=44
print(String(num, radix: 2)) //001011
配列系
配列の宣言
var numbers = Array(repeating : 0, count : 10) //要素指定で宣言できる
var array = Array(repeating : "Qiita", count : 5)
print(numbers) //[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
print(array) //["Qiita", "Qiita", "Qiita", "Qiita", "Qiita"]
配列の要素検索
var array=[1, 2, 3, 4, 5]
var count = array.filter{ $0==5 }.count
print(count) //1
配列の要素に処理を施す
var array=[1, 2, 3, 4, 5]
var array2=array.map { $0 + 100 }
print(array2) // [101, 102, 103, 104, 105]
var strArray=array.map { String($0*100) }
print(strArray) // ["100", "200", "300", "400", "500"]
配列の要素集計
var array=[1, 2, 3, 4, 5]
var sum=array.reduce(0,+) //reduce(初期値, combine: 演算子)
print(sum) // 15 合計
var multiplication=array.reduce(1,*)
print(multiplication) // 120 乗算
var average=array.reduce(0, +)/array.count
print(average) // 3 平均
配列の最大最小
var arr=[Int]()
arr+=[5, 7, 3]
var ans=arr.max() // Optional(7)
print(ans!) // 7 (アンラップする)
var ans=arr.max()! // 7 (アンラップする)
print(ans) // 7
配列に含まれているか
var array=[1, 2, 3, 4, 5]
array.contains(5) // true
array.contains(10) // fales
配列の特定の要素番号を取得
var array=[1, 2, 3, 4, 5]
// 戻り値はオプショナルのためif letを使っている
if let index=array.firstIndex(of:5) {
print(index) //4 (index番号)
print(array[index]) //5 (要素値)
}
配列の出力
var array=[1, 2, 3, 4, 5]
for i in array {
print(i)
}
//1
//2
//3
//4
//5
配列の逆順出力
var array=[1, 2, 3, 4, 5]
for i in array.reversed() {
print(i)
}
//5
//4
//3
//2
//1
//配列そのものを逆順にするとき
print(array.reverse()) //[5, 4, 3, 2, 1]
配列の挿入
var array=[1, 2, 3, 4, 5]
array.insert(10, at:3) //insert(挿入要素, at:挿入index)
print(array) //[1, 2, 3, 10, 4, 5]
array [1, 2, 3, 4, 5]
at 先頭 0, 1, 2, 3, 4, 5末尾
要素の削除
var array=[1, 2, 3, 4, 5]
array.remove(at:3) //remove(at:削除するindex)
print(array) //[1, 2, 3, 5]
var array2=[1, 2, 3, 4, 5]
array2.removeSubrange(1...2) //rengeを指定して削除
print(array2) //[1, 4, 5]
var array3=[1, 2, 3, 4, 5]
array.removeFirst() //先頭要素の削除
print(array3) //[2, 3, 4, 5]
var array4=[1, 2, 3, 4, 5]
array.removeFirst(3) //先頭から3つ要素を削除
print(array4) //[4, 5]
var array5=[1, 2, 3, 4, 5]
array.removeLast() //末尾要素の削除()引数指定で末尾からn個要素削除
print(array5) //[1, 2, 3, 4]
var array6=[1, 2, 3, 4, 5]
array.removeAll() //全要素削除
print(array6) //[]
var array7=[3, 1, 2, 3, 4, 5, 3]
array.removeAll(where: {$0==3}) //要素を指定して削除
print(array7) //[1, 2, 4, 5] //要素3がすべてないなった
参考:
【Swift】配列から条件に一致した要素を削除する方法
Swift の Array(配列)- 要素の削除
疑問
- removeFirst()とdropFirst()の違いについて
- 公式ソース:dropfirst(_:)
- 公式ソース2:removefirst()
- 戻り値に違いあり(Self.SubSequenceとSelf.Element)
- そもそもSubSequenceってなんだ?
条件系
データ有無
let arr=["Oiita","iita"]
if arr.isEmpty {
print("データが存在します") //-> データが存在します
} else {
print("データがありません")
}
let arr2=[String]()
if arr2.isEmpty {
print("データが存在します")
} else {
print("データがありません") //-> データがありません
}
switch文
let i=5
switch i {
case 1:
print("E")
case 2:
print("D")
case 3:
print("C")
case 4:
print("B")
case 5:
print("A")
default:
print()
}
// -> "A"
let arr=[100, 10, 60, 79]
for i in 0..<arr.count {
let num=arr[i]
switch num {
case (80...100): //caseは範囲指定できる
print("A")
case (70...79):
print("B")
case (60...69):
print("C")
default:
print("D")
} //switchここまで
}
// "A"
// "D"
// "C"
// "B"
参考:Swift さくっと確認したい基礎文法 [いろんな条件分岐(switch文、if文)]
While無限ループ
var i = 0
while true {
i+=1
if 4 < i {
break
}
if i==2 {
continue
}
print(i)
}
// 1 3 4
let h=Int(readLine()!)!
var p=[0, 1, 1]
var m=[0, 1, 1]
var dmg=2
var i=2
while h > dmg { // while 条件式成立するならループ
p[0] = p[1]
p[1] = p[2]
m[0] = m[1]
m[1] = m[2]
p[2] = m[0] + m[1]
m[2] = p[0] + 2*p[1]
dmg += m[2]
i += 1
}
print(i)
慣れないとスライドする思考は難しそう。
スタック・キュー
最大区間の和
let nx=readLine()!.split(separator: " ") // 4 2
let n=Int(nx[0])! // 数値の数 4
let x=Int(nx[1])! // 加算する数 2
let s=readLine()!.split(separator: " ") // 2 3 4 1
var add=[Int]()
for i in 0..<n {
add+=[Int(s[i])!] // [2 ,3, 4, 1]
}
var max_sum=0
var tmp_sum=0
var left_num=add[0] // 2
//初期区間の合計
for i in 0..<x {
max_sum+=add[i]
}
tmp_sum=max_sum //初期区間の和 5
//残り区間のリフト計算
for r in 0..<n-x {
tmp_sum-=add[r] //先頭を引く
tmp_sum+=add[r+x] //次の値を足す
if max_sum<tmp_sum { //区間の和以上のなった時更新
left_num=add[r+1] //r+1が区間の和先頭のindex
max_sum=tmp_sum
}
}
print("\(max_sum) \(left_num)") // 7 3
逆ポーランド記法
参考:【考察】なぜ Swift に popLast()
があって popFirst()
がないのか
問題:逆ポーランド記法 Swift編
let n=7
var arr=["1","2","+","3","4","+","-"]
var tmp=[Int]() // セット用配列
var num1=0
var num2=0
for i in 0..<n {
if arr[i]=="+" || arr[i]=="-" { // pop条件
num1=tmp.popLast()!
num2=tmp.popLast()!
if arr[i]=="+" {
tmp.append(num1+num2)
} else {
tmp.append(num2-num1)
}
} else {
tmp.append(Int(arr[i])!)
}
}
print(tmp[0])
エスカレーター
let nk=readLine()!.split(separator: " ")
let n=Int(nk[0])!
let k=Int(nk[1])!
var en=readLine()!.split(separator: " ")
let enlast=Int(en.last!)!
var arr=[String]()
var cnt=0
for _ in 0..<k {
arr+=[""] //k要素の配列を作成
}
for i in 1...enlast {
arr.removeFirst() // 先頭を削除
if i==Int(en[0])! { // i番目がen[0]と同じとき追加
cnt=0
arr+=[String(en[0])]
en.removeFirst() // 先頭を削除
for a in 0..<arr.count {
if arr[a] != "" {
cnt+=1 //空白以外ならカウントアップ
}
}
print(cnt)
} else {
arr+=[""]
}
}
箱とボール
問題:箱とボール
removeFirst()はO(n)に対してremoveLast(),popLast()はO(1)に注目する
let n=Int(readLine()!)!
var s=readLine()!.split(separator: " ")
var arr=[Int]()
for i in 0..<n {
arr+=[Int(s[i])!]
while arr.count>=2 { //arr要素2以上で回す
let top2=arr.popLast()!
let top1=arr.popLast()!
if top1==top2 {
//同じ値は倍にして追加
arr+=[top1*2]
} else {
//異なる値はそれぞれ追加
arr+=[top1]
arr+=[top2]
break //異なるtop1,top2を追加したらループ終了
}
}
}
for _ in 0..<arr.count {
print(arr.popLast()!)
}
スタック・キュー応用編
3 つのスタック
問題:3 つのスタック
import Foundation
let n=Int(readLine()!)!
var stack1=[Int]()
var stack2=[Int]()
var stack3=[Int]()
for _ in 0..<n{
let a=readLine()!
if a.contains("push"){
let push=a.split(separator: " ")
if push[1]=="1"{
stack1+=[Int(push[2])!]
} else {
if push[1]=="2"{
stack2+=[Int(push[2])!]
} else {
stack3+=[Int(push[2])!]
}
}
} else {
let pop=a.split(separator: " ")
if pop[1]=="1"{
stack1.popLast()!
} else {
if pop[1]=="2"{
stack2.popLast()!
} else {
stack3.popLast()!
}
}
}
}
if stack1.isEmpty==false {
for s1 in stack1.reversed(){
print(s1)
}
}
if stack2.isEmpty==false {
for s2 in stack2.reversed(){
print(s2)
}
}
if stack3.isEmpty==false {
for s3 in stack3.reversed(){
print(s3)
}
}
「配列逆順ですべて出力」答えの出力が分かりにくかった。
先頭と末尾だけ出力すればよいと解釈していた。
warp
問題:warp
import Foundation
// 入力から2つの数字(n, k)を取得
let nk = readLine()!.split(separator: " ").map{ Int($0)! }
let n = nk[0] // nを代入
let k = nk[1] // kを代入
// 配列 to の宣言
// to[i][j]はi行j列目を表す
// toはn行k列を持ち、初期値が0の2次元配列
var to = [[Int]](repeating: [Int](repeating: 0, count: k), count: n)
// 現在の位置を記録する変数 now の宣言
var now = 0
// Sは移動先を記録する
var S: [Int] = []
// n行分繰り返す
for i in 0..<n {
// i行目の入力を取得して配列 toi に代入
let toi = readLine()!.split(separator: " ").map{ Int($0)! }
// k列分繰り返す
for j in 0..<k {
// toi のj番目を-1してto[i][j]に代入
to[i][j] = toi[j] - 1
}
}
// Sの配列に初期値0を追加
S.append(0)
// k回繰り返す
for i in 0..<k {
// 現在地(now)から移動先(to[now][i])が-2の場合(=戻る)
if to[now][i] == -2 {
// Sの配列の末尾(最後に移動した場所)を削除
S.removeLast()
// Sの配列の末尾(一つ前に移動した場所)を現在地とする
now = S.last!
} else {
// 移動先を現在地とする
now = to[now][i]
// 移動先をSの配列に追加
S.append(now)
}
// 現在地を出力する
print(now + 1)
}
toはnマスをk回移動するための初期セット
to[i][j]
var to = [[Int]](repeating: [Int](repeating: 0, count: 5), count: 5)
// 出力例
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
image
問題が上記フローとなることを理解するのに1週間以上要した。
日本語難しいです。
クエリメニュー
平方分割
問題:平方分割
// 10000の平方根を初期セット
let BLOCK_SIZE = 100
// k値の入力
let k = Int(readLine()!)!
// 10000の値をまとめて受取る
let inputs = (0..<10000).map { _ in Int(readLine()!)! }
// 前処理: 各ブロックの最大値を計算する
// 要素を事前に宣言することで時短になる
// (blockMax+=[]では遅くなる)
var blockMax = [Int](repeating: -100000, count: 100)
for i in 0..<10000 {
// ブロックのインデックスを計算する
let blockIndex = i / BLOCK_SIZE
blockMax[blockIndex] = max(blockMax[blockIndex], inputs[i])
}
// k個のクエリをまとめて受取る
let queries = (0..<k).map { _ in readLine()!.split(separator: " ").map { Int($0)! } }
// クエリを高速に処理する
for query in queries {
let l = query[0] - 1
let r = query[1] - 1
let lBlock = l / BLOCK_SIZE
let rBlock = r / BLOCK_SIZE
var res = -100000
// 区間が同じブロック内の場合
if lBlock == rBlock {
for i in l...r {
res = max(res, inputs[i])
}
} else {
// 複数の区間にブロックがまたがる場合
// 左端ブロックの最大値を計算
for i in l...((lBlock + 1) * BLOCK_SIZE - 1) {
res = max(res, inputs[i])
}
// 右端ブロックの最大値を計算
for i in (rBlock * BLOCK_SIZE)...r {
res = max(res, inputs[i])
}
// 中間ブロックの最大値を計算
for i in (lBlock + 1)..<rBlock {
res = max(res, blockMax[i])
}
}
// 結果を出力する
print(res)
}
-
let blockIndex = i / BLOCK_SIZE
部分はif i % BLOCK_SIZE==0{}
にしても差は微小、ブロックのインデックスを計算するという処理の意図が明確なのは前者。 - max()関数を使用する場合は、各ブロックの最大値を計算するたびに、既存の最大値と比較して、より大きい方を保存する。max()関数を使用しない場合と比べて最大値を求めるために必要なループ回数が減り、処理が高速化される。
- 後半クエリ処理の部分でkが9000以上のときビルドパフォーマンス不足に悩んだ。
- 与えられたクエリに対して高速な処理を行うためには、ブロックサイズを適切に設定し、ブロックごとに最大値を予め計算するという方法が有効。
点と幅
問題:点の幅
let nk = readLine()!.split(separator: " ").map { Int($0)! }
let n = nk[0]
let k = nk[1]
// 生徒の得点
let inputs = (0..<n).map { _ in Int(readLine()!)! }
// 生徒番号の範囲
let queries = (0..<k).map { _ in readLine()!.split(separator: " ").map { Int($0)! } }
for query in queries {
// 生徒番号の始点終点
let aStart = query[0] - 1
let aEnd = query[1] - 1
let bStart = query[2] - 1
let bEnd = query[3] - 1
// 得点の範囲
let aRange = inputs[aStart...aEnd]
let bRange = inputs[bStart...bEnd]
// 最低点最高点
let aMin = aRange.min()!
let aMax = aRange.max()!
let bMin = bRange.min()!
let bMax = bRange.max()!
// 得点の差異
let aDiff = aMax - aMin
let bDiff = bMax - bMin
// 差異比較と結果出力
if aDiff == bDiff {
print("DRAW")
} else if aDiff > bDiff {
print("A")
} else {
print("B")
}
}
- 自分で作成した際はmin(),max()を使わずにaRange,bRangeをループしてif文にした(ケース4:0.59秒)
- 上記コードのmin(),max()関数を利用することでループとif文が減ってパフォーマンスが最適化(ケース4:0.36秒)
- 問題からどんな風にコードに落とし込むかスムーズに考えられた
ソート系
- sort()の["100", "101", "120"]など並べ替えできないのはなぜ?
参考:Swift文字列と数字で配列をソート - 基本はInt変換してソート
- 文字列でソートするときは参考内容を参照
配列の並べ替え
- sortは破壊的ソート(配列元を上書きする)
- sortedは非破壊的ソート(配列元はそのまま保持)
参考:Swiftで配列(Array)をソート(sort, sorted)
var languages: [String] = ["Swift", "Java", "Ruby", "Javascript", "PHP"]
languages.sort()
print(languages) //["Java", "Javascript", "PHP", "Ruby", "Swift"]
var numbers: [Int] = [3, 1, 4, 1, 5, 9, 2]
numbers.sort()
print(numbers) //[1, 1, 2, 3, 4, 5, 9]
// 降順はsort(by: >), 昇順はsort(by: <)
numbers.sort(by: >)
print(numbers) //[9, 5, 4, 3, 2, 1, 1]
// 明示的に記述するならクロージャ
numbers.sort(by: { $0 < $1 }) // 昇順
numbers.sort(by: { $0 > $1 }) // 降順
タプルソート
var str:[(Int,Int)]=[]
for _ in 0..<2 {
let n=readLine()!.split(separator: " ") // "1 3", "2 2"
str.append((Int(n[0])!,Int(n[1])!))
// -> str = [(1,3), (2,2)]
}
str.sort(by: {return $0 > $1}) // 降順ソート 隣通しを比較してる
// タプル場所を指定しないと1番目が同数のとき2番目でも降順ソートしている?要検証
for i in 0..<str.count {
print("\(str[i].0) \(str[i].1)") // タプルのindex指定は.index番号
}
var arr=[(_:String,num:Int)]() //空タプル宣言
arr+=[("Qiita", 1), ("kyopro", 1001), ("paiza", 9), ("tuple", 20), ("array", 100)]
arr.sort(by: {return $0.num < $1.num } ) //タプルIntの昇順ソート
// {return $0.num < $1.num } //.ラベルで数値比較を順番やっている
print(arr) // [("Qiita", 1), ("paiza", 9), ("tuple", 20), ("array", 100), ("kyopro", 1001)]
タプルのmax、min
A.最大値最小値をセットするのと、B.ソートして$0と$Nを指定するのだとどっちが速い?
A.はmaxのとき不等号の向き要注意!
後から見返したときどんな処理をしているかはAの方が分りやすいかも
//A
var items: [(name: String, number: Int)] = [("banana", 234), ("apple", 423), ("grape", 142)]
let max = items.max(by: { (a, b) -> Bool in
return a.number < b.number //ここの不等号の向き要注意!
})
let min = items.min(by: { (a, b) -> Bool in
return a.number < b.number
})
print("\(max!), \(min!)")
// -> (name: "apple", number: 423), (name: "grape", number: 142)
//B
var items2 = items.sorted(by: {return $0.1 > $1.1} )
print("\(items2[0]), \(items2[items2.count-1])")
// -> (name: "apple", number: 423), (name: "grape", number: 142)
参考:Swift:配列のmax(by:),min(by:)の使い方
タプル検索
var taple=[("A", 3), ("B", 1), ("C", 6)]
let users=["A","B","C"]
print("1回目")
for i in 0..<users.count {
//条件合致のタプルindexを代入
if let index=taple.firstIndex(where: { $0.0 == users[i]}) {
print("\(users[i])のスタミナは残り\(taple[index].1)回です")
//taple[index]を利用して値の変更も可能
taple[index].1=taple[index].1-1
}
}
print("2回目")
for i in 0..<users.count {
//条件合致のタプルindexを代入
if let index=taple.firstIndex(where: { $0.0 == users[i]}) {
print("\(users[i])のスタミナは残り\(taple[index].1)回です")
}
}
//1回目
//Aのスタミナは残り3回です
//Bのスタミナは残り1回です
//Cのスタミナは残り6回です
//2回目
//Aのスタミナは残り2回です
//Bのスタミナは残り0回です
//Cのスタミナは残り5回です
参考:【swift】 タプルの配列で指定した要素のindexを取得する。
辞書
row | p | q | r | row | p | q | r | |
---|---|---|---|---|---|---|---|---|
1 | 2 | 2 | 2 | 1 | 2 | 3 | 4 | |
2(ab) | 2 | 1 | 2(ab) | 1 | 3 | |||
3(ab) | 1 | 2 | 3(ab) | 2 | 1 | |||
4(bc) | 1 | 1 | 4(bc) | 2 | 3 | |||
5(bc) | 2 | 2 | 5(bc) | 3 | 3 | |||
6(bc) | 1 | 4 | ||||||
ans(ac) | 1 | 2 | ans(ac) | 1 | 3 | |||
ans(ac) | 2 | 1 | ans(ac) | 2 | 4 |
問題文を理解するのが大変だった
let input_line=readLine()!.split(separator: " ")
let p=Int(input_line[0])!
let q=Int(input_line[1])!
let r=Int(input_line[2])!
var ab=[(Int,Int)]()
var bc=[(Int,Int)]()
for _ in 0..<p {
let a=readLine()!.split(separator: " ")
ab.append((Int(a[0])!,Int(a[1])!))
}
ab.sort(by: {return $0 < $1}) // 昇順sort
for _ in 0..<q {
let b=readLine()!.split(separator: " ")
bc.append((Int(b[1])!,Int(b[0])!)) // cb順に並べてcの昇順にする
}
bc.sort(by: {return $0 < $1}) // 昇順sort
for v in 0..<p {
for i in 0..<q {
if ab[v].1==bc[i].1 { // a b==b c 共通のbをkeyにして出力する
print("\(ab[v].0) \(bc[i].0)")
break
}
}
}
辞書のソート&出力(順序付き辞書)
dic=[("bcd", 4), ("abc", 1)] //辞書のままだと順不同
let sortedDic = dic.sorted() { $0.key < $1.key } //文字で降順、配列化する
//配列みたいに{ $0.0 < $1.0 }でもできる
//keyソートなら(by: <)も可能
print(sortedDic)
//[(key: "abc", value: 1), (key: "bcd", value: 4)]
for (key, value) in sortedDic {
print("\(key) \(value)")
}
//abc 1
//bcd 4
参考:[Swift] 順序付き辞書、DictionaryLiteral
【Swift】辞書型(Dictionary)をキー値でソートさせる方法!sortedメソッドの使い方
辞書の要素毎に値をセットする
let n=5
let str=["1", "2", "3", "3", "6"]
var num: [String: Int] = ["0": 0, "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "6": 0, "7": 0, "8": 0, "9": 0] //辞書で0...9の<Key:cnt>を宣言
for i in 0..<str.count {
let a=String(str[i]) //index部分にセットするKey値
num[a]!+=1 //辞書Key指定して特性したValueはOptional型だからアンラップする
} //print(num["0"]) -> Optional(0)
//print(num["0"]!) -> 0
for i in 0...9 {
if i==9 {
print(num[String(i)]!)
} else {
print(num[String(i)]!, terminator: " ")
}
}
// 0 1 1 2 0 0 1 0 0 0
for (key, value) in num { //こんな風にも表せるが順番はハチャメチャ
print("\(key): \(value)", terminator: " ")
}
//6: 1 7: 0 8: 0 9: 0 0: 0 2: 1 4: 0 5: 0 3: 2 1: 1
print(num)//順番ははちゃめちゃ
//["5": 0, "0": 0, "4": 0, "7": 0, "6": 1, "9": 0, "1": 1, "2": 1, "3": 2, "8": 0]
辞書KeyはIntの方がラク?Key指定時に
参考:辞書 | Dictionary型 - Swiftの始め方
辞書のprint方法
英小文字の出現率
ごり押しせずにArrayキャストかつsortedで順番に出力できる
let n=Int(readLine()!)!
let str=Array(readLine()!)
var check: [String:Int]=["a":0, "b":0, "c":0, "d":0, "e":0, "f":0, "g":0, "h":0,
"i":0, "j":0, "k":0, "l":0, "m":0, "n":0, "o":0, "p":0, "q":0, "r":0,
"s":0, "t":0, "u":0, "v":0, "w":0, "x":0, "y":0, "z":0]
for i in 0..<str.count {
let key=String(str[i])
check[key]!+=1
}
print("\(check["a"]!) \(check["b"]!) \(check["c"]!) \(check["d"]!) ", terminator: "")
print("\(check["e"]!) \(check["f"]!) \(check["g"]!) \(check["h"]!) \(check["i"]!) ", terminator: "")
print("\(check["j"]!) \(check["k"]!) \(check["l"]!) \(check["m"]!) \(check["n"]!) ", terminator: "")
print("\(check["o"]!) \(check["p"]!) \(check["q"]!) \(check["r"]!) \(check["s"]!) ", terminator: "")
print("\(check["t"]!) \(check["u"]!) \(check["v"]!) \(check["w"]!) \(check["x"]!) ", terminator: "")
print("\(check["y"]!) \(check["z"]!)")
参考:[Swift] Dictionaryをこねくり回すネタ集
辞書の要素追加
let nm=readLine()!.split(separator: " ").map{Int($0)!} //"3 4"から["3", "4"]から[3, 4]へ変換
let n=nm[0]
let m=nm[1]
var dic=[String:Int]()
for _ in 0..<n {
let ab=readLine()!.split(separator: " ")
dic[String(ab[0])]=Int(ab[1])! //辞書keyに対してvalueをセット
//keyが存在しないなら追加され、存在する場合valueが更新する
}
var chkList=[String]()
for _ in 0..<m {
let s=readLine()!
chkList+=[s]
}
for i in chkList {
if dic[i]==nil { //この書き方はもっとよくできそう↓参照
print(-1)
} else {
print(dic[i]!)
}
}
//改善したスマート
for i in chkList {
print(dic[i] ?? -1)
}
参考:【Swift入門】Dictionaryの要素を更新・追加・削除する方法
辞書key,value存在チェック
let nq=readLine()!.split(separator: " ").map {Int($0)!}
let n=nq[0]
let q=nq[1]
var sarr=[String:Int]()
for i in 1...n {
let s=readLine()!
if sarr.keys.contains(s) { //keyがあればtrue
//何もしない key重複時はvalue更新したくない
} else {
sarr[s]=i //初出keyのみvalue追加
}
}
for _ in 0..<q {
let q=readLine()!
print(sarr[q] ?? -1) //辞書keyよりvalue表示
}
参考:Swift の Dictionary(辞書)- キーと値のペア数とキーの存在確認
valueが複数要素の辞書
問題:銀行 Swift編
辞書は順序保持できないため登録順番をValueへ追加した
SPMでCollectionを利用できる場合はOrderedDictionaryで順序保持できそう
let nk=readLine()!.split(separator: " ").map {Int($0)!}
let n=nk[0]
let k=nk[1]
var array = [String:[Int]]()
// 銀行名:[暗証番号,残高,登録順)で辞書作成
for i in 0..<n{
let q=readLine()!.split(separator: " ")
let companyName=String(q[0])
let password=Int(q[1])!
let balance=Int(q[2])!
array[companyName]=[password,balance,i]
}
for _ in 0..<k{
let t=readLine()!.split(separator: " ")
let companyName=String(t[0])
let password=Int(t[1])!
let balance=Int(t[2])!
// 暗証番号一致のとき(array[]?[index]この書き方は辞書独特なのか.じゃないので注意)
if password==array[companyName]?[0] {
// 残高更新する(valueの数値を利用するときアンラップが必要)
array[companyName]=[password,(array[companyName]?[1])!-balance,(array[companyName]?[2])!]
}
}
// 登録順昇順ソート
for (key,value) in array.sorted(by: { $0.value[2] < $1.value[2] }){
print("\(key) \(value[1])")
}
value配列の要素追加
問題:経理 Swift編
var allArray=[String: [(String,Int)]]()の場合、keyは順不同だがvalueは順番を保持できる
要素追加はallArray[key]?.append(value)
let nk=readLine()!.split(separator: " ").map {Int($0)!}
let n=nk[0]
let k=nk[1]
var postArray=[String]()
var allArray=[String: [(String,Int)]]() // (部課):[(注文番号,経費)]
for _ in 0..<n{
let post=readLine()!
postArray+=[post]
// keyを先に作成しておく
allArray[post]=[("",0)]
}
for _ in 0..<k{
let costs=readLine()!.split(separator: " ")
let post=String(costs[0])
let order=String(costs[1])
let cost=Int(costs[2])!
// key指定してvalue要素を追加 allArray[post]?+=[(order,cost)]も同じ
allArray[post]?.append((order,cost))
}
for key in postArray{
print(key)
// valueのindex0番目("",0)を除外
for value in allArray[key]! {
if value.0 != "" {
print("\(value.0) \(value.1)")
}
}
print("-----")
}
当初for _ in 0..<k{...}の中でallArrayを下記のように作ろうとしたところタイムオーバーになった
下記のようなforループ内でのcontains()は極力避けたほうが良い
if allArray.keys.contains(post)==false {
// keyがなければ新規
allArray[post]=[(order,cost)]
} else {
// keyがあれば要素追加
allArray[post]?.append((order,cost))
}
Set型
- 一意な値を格納する
- 順序関係は保証されない
- 論理演算(積集合など)はSetがはやい
- 集合の生成(追加など)はArrayがはやい
参考:
Swift の Set(セット)で集合の演算
Setは遅いのか
Setを使いこなしたい(願望)
Setで配列値の重複チェック
var arr1=[1, 14, 3, 20, 6, 12, 14]
var arr2=[14, 12, 11, 18]
//ArrayからSet型へキャスト
var setarr1=Set(arr1) //重複値はなくなる
print(setarr1) //[1, 14, 3, 20, 6, 12]
var setarr2=Set(arr2) //順番もかわる
print(setarr2) //[12, 11, 18, 14]
//積集合
var setarr3=setarr1.intersection(setarr2)
print(setarr3) //[14, 12]
//和集合
var setarr4=setarr1.union(setarr2)
print(setarr4) //[11, 1, 14, 18, 3, 20, 6, 12]
//差集合
var setarr5=setarr1.subtracting(setarr3) //setarr1から重複値を削除する
print(setarr5) //[3, 6, 20, 1]
//要素が含まれているかチェック
print(setarr4.contains(14)) //true
print(setarr4.contains(0)) //false
//集合要素の追加・削除
print(setarr3.insert(100)) //[12, 14, 100]
print(setarr3.remove(12)) //[100, 14]
let a: Set<Element> = 値
let a: Set<Int> = [1,2,3]
let a: Set<Int> = [] //空集合
let a = Set<Int>() //空集合
let arr=Array(a) //配列へキャスト
動的配列
import Foundation //忘れない
let nq=readLine()!.split(separator: " ")
var a=readLine()!.split(separator: " ").map{String($0)}//一括[String]変換
let n=Int(nq[0])!
let q=Int(nq[1])!
for _ in 0..<q {
var q1=0 //for内と外どちらで宣言する方が良い?
var q2=""
let qry=readLine()!
if qry.contains("0") {
let qrysplit=qry.split(separator: " ")
q1=Int(qrysplit[0])!
q2=String(qrysplit[1])
} else {
q1=Int(qry)!
}
if q1==0 {
// push_back x
a.append(q2)
}
if q1==1 {
// push_back x
a.popLast()!
}
if q1==2 {
//print
for s in 0..<a.count {
if s==a.count-1 {
print(a[s])
} else {
print(a[s], terminator: " ")
}
}
}
}
配列の要素毎に値をセットする
数値の出現率
初見ではfor文で多重ループ処理してごり押した。
let n=5
let str=["1", "2", "3", "3", "6"]
var arr=[0,0,0,0,0,0,0,0,0,0] //0...9の10要素カウント用配列
for i in 0..<str.count {
arr[str[i]]+=1 //strの値をarr[index]として+1する
}
for i in 0..<arr.count {
if i==arr.count-1 {
print(arr[i])
} else {
print(arr[i],terminator: " ")
}
}
// 0 1 1 2 0 0 1 0 0 0
構造体
構造体の検索
struct User:Equatable{ //この問題ではEquatableなしでも通る
var nickname : String;
var old : String;
var birth : String;
var state : String;
}
let n=3 //任意の数
var arr=[User]() // Userを格納する配列
for _ in 0..<n {
let qry=readLine()!.split(separator: " ").map {String($0)}
let test=User(nickname: qry[0],old: qry[1], birth: qry[2], state: qry[3])
arr+=[test]
}
let o="23" //年齢を探す
var ans=arr.first(where: { $0.old==o }) //構造体内で最初に該当する要素indexを取得
print(ans!.nickname) //アンラップしてから指定する
【Swift】構造体の要素を検索してインデックスを取得する方法!
構造体の更新
struct User{
var stid : Int; //ユニークなIDをセット
var nickname : String;
var old : String;
var birth : String;
var state : String;
}
let nk=readLine()!.split(separator: " ").map {Int($0)!}
let n=nk[0]
let k=nk[1]
var arr=[User]()
for i in 1...n {
let qry=readLine()!.split(separator: " ").map {String($0)}
let test=User(stid:i, nickname: qry[0],old: qry[1], birth: qry[2], state: qry[3])
arr+=[test]
}
for _ in 0..<k {
let ann=readLine()!.split(separator: " ").map {String($0)}
let stid=Int(ann[0])!
let nn=ann[1]
changeName(index:stid, chgName:nn)
}
func changeName(index:Int, chgName:String) {
arr[index-1].nickname=chgName //arrのindex番号はstid-1となる
//配列のindex番号を指定して該当行のnicknameを更新する
}
for i in 0..<arr.count {
print("\(arr[i].nickname) \(arr[i].old) \(arr[i].birth) \(arr[i].state)")
}
参考:【Swift】構造体の要素を検索してインデックスを取得する方法!
クラス
クラスの作成
class Employee {
let number:Int //初期値があればinit()はいらない?
let name:String //初期値なしの場合はinit()必須
var array=[(Int,String)]()
init() {
self.number=0
self.name=""
}
func make(number:Int,name:String) {
array+=[(number,name)]
}
func getnum(num:Int) {
print(array[num-1].0)
}
func getname(num:Int) {
print(array[num-1].1)
}
}
let n=Int(readLine()!)!
var c=Employee() //Employeeクラスを使えるようにする
for _ in 0..<n {
let s=readLine()!.split(separator: " ")
let f=String(s[0])
let number=Int(s[1])!
if f == "make" {
c.make(number:number, name:String(s[2]))
} else {
if f == "getnum" {
c.getnum(num:number)
} else {
c.getname(num:number)
}
}
}
Activity history
date | value |
---|---|
2022/09/15 | Swift スキルチェックDランク始動 |
2022/11/08 | 問題集:Cランクレベルアップメニュー修了 |
2022/11/24 | スキルチェックDランク問題218問修了 |
2022/11/28 | ランクB達成 |
2022/11/29 | 問題集:スタック・キューメニュー修了 |
2022/12/06 | paiza課金開始 |
2022/12/12 | 問題集:データセット選択メニュー修了 |
2022/12/27 | 問題集:論理演算メニュー修了 |
2023/01/06 | 問題集:配列活用メニュー修了 |
2023/03/23 | 問題集:クエリメニュー修了 |