소피it블로그

[Swift] 코딩테스트로 스위프트 공부하기 - 1차원 배열 (1) 본문

개발_iOS/코딩테스트로 스위프트 공부하기

[Swift] 코딩테스트로 스위프트 공부하기 - 1차원 배열 (1)

sophie_l 2022. 4. 16. 18:51

1. 최소, 최대

https://www.acmicpc.net/problem/10818

 

10818번: 최소, 최대

첫째 줄에 정수의 개수 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 N개의 정수를 공백으로 구분해서 주어진다. 모든 정수는 -1,000,000보다 크거나 같고, 1,000,000보다 작거나 같은 정수이다.

www.acmicpc.net

간단한 문제지만 짚고 넘어가고 싶은 부분이 딱 하나 있어서 적는다.

총 두 가지 풀이를 적용해보았다.

긴 풀이

첫 번째는 정직하게 일일이 비교해주는 풀이. 채점이 이렇게 오래 걸릴 일인가 싶을 정도로 오래 걸렸지만, 보아하니 다른 사람들 풀이도 다들 400ms정도는 나오는 것 같았다.

이 풀이에 대해서는 딱히 짚고 넘어갈 게 없으니 두 번째 풀이를 보자.

간단한 풀이

이 풀이는 nums.max()와 nums.min()을 이용한 간단한 풀이였는데, 주의할 점이 하나 있었다. 저번에도 짚고 넘어갔던 부분이지만 max()와 min()의 리턴값이 옵셔널이라는 점. 따라서 언래핑을 반드시 해주어야 한다. 코딩테스트에 한해 강제언래핑을 하기로 했으니(시간 효율상) 강제언래핑한 후 그 값을 최대, 최솟값에 대입해주었다.

역시 채점에는 400ms가량 걸려, 꽤 오래 기다려야 했다. 그렇지만 보통 이정도는 나오는듯.


2. 최댓값

https://www.acmicpc.net/problem/2562

 

2562번: 최댓값

9개의 서로 다른 자연수가 주어질 때, 이들 중 최댓값을 찾고 그 최댓값이 몇 번째 수인지를 구하는 프로그램을 작성하시오. 예를 들어, 서로 다른 9개의 자연수 3, 29, 38, 12, 57, 74, 40, 85, 61 이 주어

www.acmicpc.net

역시 두 가지 풀이를 제출하였다.

굳이 배열화하지 않은 풀이

첫 풀이는 딱히 코멘트할 부분이 없으니 패스

배열로 만들어서 푼 풀이

빈 배열 만드는 부분과, 요소의 인덱스값 가져오기 부분에서 살짝씩 헷갈렸다.

빈 배열 초기화하기는 저번에도 한번 짚고 넘어갔던 부분인데 오랜만에 하려니 또 헷갈려서, 

var emptyArr1: [Int] = []
var emptyArr2 = Array<Int>()
var emptyArr3 = [Int]()

한 번 더 정리하고 넘어가자.

그리고 오늘 처음 알게 된 부분이 있었는데, 배열에서 요소의 인덱스를 가져오려면 이전 버전에서는 index(of: )를 사용하면 되었다고 하나 요즘에는 firstIndex(of: ) 혹은 lastIndex(of: )를 사용해야 한다고 한다. 그리고 또 중요한 점 한 가지는 그렇게 가져온 인덱스값은 옵셔널이라는 점. 따라서 언래핑을 해줘야 한다. 아마 해당 요소가 배열에 없을 가능성도 있기 때문이겠거니 싶지만 스위프트는 매번 옵셔널로 사람을 헤매게 만드는 것 같아서 야속한 기분도 든다.


3. 숫자의 개수

https://www.acmicpc.net/problem/2577

 

2577번: 숫자의 개수

첫째 줄에 A, 둘째 줄에 B, 셋째 줄에 C가 주어진다. A, B, C는 모두 100보다 크거나 같고, 1,000보다 작은 자연수이다.

www.acmicpc.net

하.. 이 문제만 얼마나 오래 들여다봤는지.... 스위프트의 Character는 다루기 왜이리 까다로운걸까?_? 내가 아직 잘 몰라서 그러는 걸까??

 

우선 아무리 피하려해도 계속 만났던 오류 먼저 보자.

아무리 해도 제거할 수 없던 오류😡

가장 먼저, 이번에 처음 배운 filter함수를 보자.

스위프트에서는 파이썬에서와 다르게 count메서드에 개수 세기를 원하는 요소명을 인수로 전달해줄 수가 없었다. 즉, 파이썬의 경우 myString.count("1")와 같이 문자열에서 특정 요소의 개수를 바로 검색할 수 있었던 반면, 스위프트에서는 myString.count("1")은 쓸 수 없었고 count는 오직 myString.count 처럼 전체 요소의 개수를 구하는 식으로만 쓸 수 있었다.

a * b * c를 스트링 mul로 변환한 후 0부터 9까지 각 숫자가 mul 안에 몇 개씩 들어있는지 세려고 했지만 count로 특정 요소의 개수만을 셀 수는 없었길래 고민하다가 검색을 하게 되었다. 그 결과 찾게 된 방법이 바로 filter 함수이다.

let myString = "I need to find how many e this sentence contains."

라는 문자열에서 "e"의 개수를 세야 한다고 하면

 

오류

파이썬에서처럼 하려고 하면 오류가 나기 때문에

filter 써줘야함

이렇게 스위프트 식으로 filter를 써줘야 한다. 물론 파이썬에도 필터가 있긴 하지만.

다시 정리하자면,

myString.filter { $0 == "e" }.count

// 이렇게 해줘야 한다는 것.

여기까진 했는데, 이를 for 문에 넣어서 for i in 0...9 { print(mul.filter { $0 == "\(i)" }.count) } 처럼 응용하려고 하니 계속 상단의 오류가 나면서 진행이 되지 않았다. 뭐가 문제인지를 모르겠어서

for i in 0...9 {
	let iStr = String(i)
    	print(mul.filter { $0 == iStr }.count)
}

라고 변형을 시켜줬지만 역시나 차이는 없었다.

 

for문 바깥에서 i 없이 직접 "1" 등 숫자 자체로 비교해주니까 제대로 작동하길래 정말 어쩔 수 없이 for문을 포기하고 0부터 9까지 각 경우에 대해 filter함수를 총 10번 써줘야하나 고민이 들던 찰나,

이 오류가 String과 Character를 제대로 구별하지 않아서 생긴 오류라는 생각이 들었다. 그 전엔 오류문구를 봐도 뭔소린지 이해가 안갔는데🥲

 

확인해보니 "\(i)"는 String인 반면 mul에서 $0로 가져온 요소는 String이 아니라 String.Element aka Character이기 때문에 두 개를 직접 비교해줄 수가 없는 모양이었다.

 

다시 시도하는 과정에서 안타깝게도 String과 달리 Character는 Character(숫자)처럼 바로 문자로 형 변환을 시켜줄 수 없다는 것을 배웠다.

그래서 머리를 굴린 끝에 나온 것이 하단과 같은 풀이.

이리 해도 안되고 저리 해도 안되길래 생고생하다가 간신히 발견한 풀이. 가운데의 주석은 확인용 코드라 무시해도 된다.

filter 안에 들어있는 $0가 String이 아닌 Character이기 때문에 String과 비교가 안된다면,

String이 아닌 Character과는 비교를 해줄 수 있다는 것이겠지.

따라서 for i in 0...9를 포기하는 대신

"0123456789"라는 새로운 String을 만든 후 그 문자열의 엘리먼트 aka 문자 Character를 가져와 $0과 비교해주는 방법을 썼고, 다행히 성공했다.

정말 열받는다....... 스위프트의 문자열과 문자.... 알면 알 수록.............. 파이썬처럼 문자열 다루기 쉽게 좀 만들면 안되나😡