문제 설명

길이가 같은 두 개의 큐가 주어집니다. 하나의 큐를 골라 원소를 추출(pop)하고, 추출된 원소를 다른 큐에 집어넣는(insert) 작업을 통해 각 큐의 원소 합이 같도록 만들려고 합니다. 이때 필요한 작업의 최소 횟수를 구하고자 합니다. 한 번의 pop과 한 번의 insert를 합쳐서 작업을 1회 수행한 것으로 간주합니다.

큐는 먼저 집어넣은 원소가 먼저 나오는 구조입니다. 이 문제에서는 큐를 배열로 표현하며, 원소가 배열 앞쪽에 있을수록 먼저 집어넣은 원소임을 의미합니다. 즉, pop을 하면 배열의 첫 번째 원소가 추출되며, insert를 하면 배열의 끝에 원소가 추가됩니다. 예를 들어 큐 [1, 2, 3, 4]가 주어졌을 때, pop을 하면 맨 앞에 있는 원소 1이 추출되어 [2, 3, 4]가 되며, 이어서 5를 insert하면 [2, 3, 4, 5]가 됩니다.

다음은 두 큐를 나타내는 예시입니다.

queue1 = [3, 2, 7, 2]
queue2 = [4, 6, 5, 1]

두 큐에 담긴 모든 원소의 합은 30입니다. 따라서, 각 큐의 합을 15로 만들어야 합니다. 예를 들어, 다음과 같이 2가지 방법이 있습니다.

  1. queue2의 4, 6, 5를 순서대로 추출하여 queue1에 추가한 뒤, queue1의 3, 2, 7, 2를 순서대로 추출하여 queue2에 추가합니다. 그 결과 queue1은 [4, 6, 5], queue2는 [1, 3, 2, 7, 2]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 7번 수행합니다.
  2. queue1에서 3을 추출하여 queue2에 추가합니다. 그리고 queue2에서 4를 추출하여 queue1에 추가합니다. 그 결과 queue1은 [2, 7, 2, 4], queue2는 [6, 5, 1, 3]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 2번만 수행하며, 이보다 적은 횟수로 목표를 달성할 수 없습니다.

따라서 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수는 2입니다.

길이가 같은 두 개의 큐를 나타내는 정수 배열 queue1, queue2가 매개변수로 주어집니다. 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수를 return 하도록 solution 함수를 완성해주세요. 단, 어떤 방법으로도 각 큐의 원소 합을 같게 만들 수 없는 경우, -1을 return 해주세요.


제한사항
  • 1 ≤ queue1의 길이 = queue2의 길이 ≤ 300,000
  • 1 ≤ queue1의 원소, queue2의 원소 ≤ 109
  • 주의: 언어에 따라 합 계산 과정 중 산술 오버플로우 발생 가능성이 있으므로 long type 고려가 필요합니다.

내가 푼 풀이

- queue1 의 합 = queue2 의 합 이도록 만드는 최소의 큐 이동횟수를 구한다.

- 두 큐의 합을 비교했을때 큰 곳에서 pop, 작은곳에서 append 해서 최적의 해를 구한다.

- Swift는 큐를 직접 구현해야한다. 기능만 대충 흉내낼수 있게 코딩했다.

- 두 큐는 Array로 만들었고, arr 의 가장 앞의 원소를 pop 하는데 시간복잡도가 O(N)이다.

- removeFirst() 를 이용해서 빼면 무조건 시간초과가 뜨므로 따로 인덱스를 만들어 pop할 경우에 +1을 해서 현재 위치를 저장한다.

- q의 최대 이동횟수는 q1 에서 q2로 모두 이동하고, q2에서 q1으로 다시 모두 이동하는 횟수 q.count * 3 을 최대로 정했다.

 

import Foundation

func solution(_ queue1:[Int], _ queue2:[Int]) -> Int {
    var q1 = queue1
    var q2 = queue2
    var q1Idx = 0
    var q2Idx = 0
    var sumQ1 = q1.reduce(0, +)
    var sumQ2 = q2.reduce(0, +)
    var result = 0

    for i in 0..<queue1.count * 3 {
        var num = 0
        if sumQ1 > sumQ2 {
            num = q1[q1Idx]
            q1[q1Idx] = 0
            q1Idx += 1
            sumQ1 -= num
            sumQ2 += num
            q2.append(num)
        } else if sumQ2 > sumQ1 {
            num = q2[q2Idx]
            q2[q2Idx] = 0
            q2Idx += 1
            sumQ2 -= num
            sumQ1 += num
            q1.append(num)
        } else {
            return result
        }
        result += 1
    }
    
    return -1
}

문제 설명

레스토랑을 운영하던 스카피는 코로나19로 인한 불경기를 극복하고자 메뉴를 새로 구성하려고 고민하고 있습니다.
기존에는 단품으로만 제공하던 메뉴를 조합해서 코스요리 형태로 재구성해서 새로운 메뉴를 제공하기로 결정했습니다. 어떤 단품메뉴들을 조합해서 코스요리 메뉴로 구성하면 좋을 지 고민하던 "스카피"는 이전에 각 손님들이 주문할 때 가장 많이 함께 주문한 단품메뉴들을 코스요리 메뉴로 구성하기로 했습니다.
단, 코스요리 메뉴는 최소 2가지 이상의 단품메뉴로 구성하려고 합니다. 또한, 최소 2명 이상의 손님으로부터 주문된 단품메뉴 조합에 대해서만 코스요리 메뉴 후보에 포함하기로 했습니다.

예를 들어, 손님 6명이 주문한 단품메뉴들의 조합이 다음과 같다면,
(각 손님은 단품메뉴를 2개 이상 주문해야 하며, 각 단품메뉴는 A ~ Z의 알파벳 대문자로 표기합니다.)

가장 많이 함께 주문된 단품메뉴 조합에 따라 "스카피"가 만들게 될 코스요리 메뉴 구성 후보는 다음과 같습니다.

[문제]

각 손님들이 주문한 단품메뉴들이 문자열 형식으로 담긴 배열 orders, "스카피"가 추가하고 싶어하는 코스요리를 구성하는 단품메뉴들의 갯수가 담긴 배열 course가 매개변수로 주어질 때, "스카피"가 새로 추가하게 될 코스요리의 메뉴 구성을 문자열 형태로 배열에 담아 return 하도록 solution 함수를 완성해 주세요.

[제한사항]

  • orders 배열의 크기는 2 이상 20 이하입니다.
  • orders 배열의 각 원소는 크기가 2 이상 10 이하인 문자열입니다.
    • 각 문자열은 알파벳 대문자로만 이루어져 있습니다.
    • 각 문자열에는 같은 알파벳이 중복해서 들어있지 않습니다.
  • course 배열의 크기는 1 이상 10 이하입니다.
    • course 배열의 각 원소는 2 이상 10 이하인 자연수가 오름차순으로 정렬되어 있습니다.
    • course 배열에는 같은 값이 중복해서 들어있지 않습니다.
  • 정답은 각 코스요리 메뉴의 구성을 문자열 형식으로 배열에 담아 사전 순으로 오름차순 정렬해서 return 해주세요.
    • 배열의 각 원소에 저장된 문자열 또한 알파벳 오름차순으로 정렬되어야 합니다.
    • 만약 가장 많이 함께 주문된 메뉴 구성이 여러 개라면, 모두 배열에 담아 return 하면 됩니다.
    • orders와 course 매개변수는 return 하는 배열의 길이가 1 이상이 되도록 주어집니다.

내가 푼 풀이

- 1번 손님이 ABCFG 를 주문했다면 2개 요리의 코스메뉴는 AB, AC, AF, AG, BC, ... , FG 이다.

- 코스요리의 음식갯수대로 문자조합을 추출해서 Dictionary에 저장한다.

- 이미 앞손님이 시켰다면 ( dict[a] != nil ) +1 을 더해주고, 시키지 않았다면 초기화를 해준다.

- 주문들의 문자 조합을 모두 완성하고, value의 최댓값인 key를 결과배열에 저장한다. 

- value 가 최댓값이지만 1이라면 한 손님만 시킨 음식조합이므로 결과배열에 넣지않는다.

- 결과배열을 오름차순으로 정렬해서 출력한다.

- 조합은 Swift에서 제공하지않아서, 직접 구현할 줄 알아야한다.

 

import Foundation

func combi (_ strs: [String], _ count: Int) -> [[String]] {
    var result = [[String]]()
    
    func combination(_ index: Int, _ nowCombi: [String]) {
        if nowCombi.count == count {
            result.append(nowCombi)
            return
        }
        for i in index..<strs.count {
            combination(i+1, nowCombi + [strs[i]])
        }
    }
    combination(0, [])
    
    return result
}

func solution(_ orders:[String], _ course:[Int]) -> [String] {
    var result = [String]()
    
    for i in course {
        var dict = [String: Int]()
        var count = i
        
        for j in orders {
            var arr = String(j).map{String($0)}
            var combiArr = combi(arr, count)
            for k in combiArr {
                var str = k.sorted{$0 < $1}.joined(separator: "")
                if dict[str] == nil {
                    dict[str] = 1
                } else {
                    dict[str]! += 1
                }
            }  
        }
        
        var sortedDict = dict.sorted{ $0.value > $1.value}
        
        if dict.values.max() == nil {
            continue
        } else {
            for (key, value) in sortedDict {
                if value == dict.values.max()! && value != 1 {
                    result.append(key)
                } else {
                    break
                }
            }
        }
    }
    return result.sorted{ $0 < $1}
}

문제 설명

철수는 롤케이크를 두 조각으로 잘라서 동생과 한 조각씩 나눠 먹으려고 합니다. 이 롤케이크에는 여러가지 토핑들이 일렬로 올려져 있습니다. 철수와 동생은 롤케이크를 공평하게 나눠먹으려 하는데, 그들은 롤케이크의 크기보다 롤케이크 위에 올려진 토핑들의 종류에 더 관심이 많습니다. 그래서 잘린 조각들의 크기와 올려진 토핑의 개수에 상관없이 각 조각에 동일한 가짓수의 토핑이 올라가면 공평하게 롤케이크가 나누어진 것으로 생각합니다.

예를 들어, 롤케이크에 4가지 종류의 토핑이 올려져 있다고 합시다. 토핑들을 1, 2, 3, 4와 같이 번호로 표시했을 때, 케이크 위에 토핑들이 [1, 2, 1, 3, 1, 4, 1, 2] 순서로 올려져 있습니다. 만약 세 번째 토핑(1)과 네 번째 토핑(3) 사이를 자르면 롤케이크의 토핑은 [1, 2, 1], [3, 1, 4, 1, 2]로 나뉘게 됩니다. 철수가 [1, 2, 1]이 놓인 조각을, 동생이 [3, 1, 4, 1, 2]가 놓인 조각을 먹게 되면 철수는 두 가지 토핑(1, 2)을 맛볼 수 있지만, 동생은 네 가지 토핑(1, 2, 3, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것이 아닙니다. 만약 롤케이크의 네 번째 토핑(3)과 다섯 번째 토핑(1) 사이를 자르면 [1, 2, 1, 3], [1, 4, 1, 2]로 나뉘게 됩니다. 이 경우 철수는 세 가지 토핑(1, 2, 3)을, 동생도 세 가지 토핑(1, 2, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것입니다. 공평하게 롤케이크를 자르는 방법은 여러가지 일 수 있습니다. 위의 롤케이크를 [1, 2, 1, 3, 1], [4, 1, 2]으로 잘라도 공평하게 나뉩니다. 어떤 경우에는 롤케이크를 공평하게 나누지 못할 수도 있습니다.

롤케이크에 올려진 토핑들의 번호를 저장한 정수 배열 topping이 매개변수로 주어질 때, 롤케이크를 공평하게 자르는 방법의 수를 return 하도록 solution 함수를 완성해주세요.


제한사항
  • 1 ≤ topping의 길이 ≤ 1,000,000
    • 1 ≤ topping의 원소 ≤ 10,000

내가 푼 풀이

- topping의 길이가 최대 1,000,000 이라서 2중 for문은 무조건 시간초과가 뜰꺼라 생각했다.

- 처음에는 for문으로 topping[0...i] 와 topping[i+1..<topping.count] 두 배열을 Set으로 만들어 중복을 제거하고 갯수 확인으로 검사했다. ----> 역시나 시간초과

- Dictionary[Int: Int] 로 처음에 총 토핑과 토핑의갯수를 구했다.

- 배열의 앞부터 잘라서 넣어놓을 Set 하나를 선언해두고 배열을 순회할때마다 잘라서 Set에 넣어주고 Dictionary에는 토핑과 그토핑의 갯수를 빼줬다.

- Dictionary에 만약 토핑의 갯수가 0 이라면 지우고, Dictionary의 갯수와 Set의 갯수가 같으면 공평하다 -> result += 1

- result 출력

 

 

import Foundation

func solution(_ topping:[Int]) -> Int {
    var result = 0
    var arr = topping
    var set1 = Set<Int>()
    var dict = [Int: Int]()
    for i in 0..<topping.count {
        if dict[topping[i]] == nil {
            dict[topping[i]] = 1
        } else {
            dict[topping[i]]! += 1
        }
    }
    for i in 0..<topping.count {
        set1.insert(topping[i])
        dict[topping[i]]! -= 1
        if dict[topping[i]]! == 0 {
            dict[topping[i]] = nil
        }
        if set1.count == dict.count {
            result += 1
        }
    }

    return result
}

문제 설명

자연수 x를 y로 변환하려고 합니다. 사용할 수 있는 연산은 다음과 같습니다.

  • x에 n을 더합니다
  • x에 2를 곱합니다.
  • x에 3을 곱합니다.

자연수 x, y, n이 매개변수로 주어질 때, x를 y로 변환하기 위해 필요한 최소 연산 횟수를 return하도록 solution 함수를 완성해주세요. 이때 x를 y로 만들 수 없다면 -1을 return 해주세요.


제한사항
  • 1 ≤ x  y ≤ 1,000,000
  • 1 ≤ n < y

내가 푼 풀이

- 모든 경우의 수를 구하는 방식으로 중복을 제거하는 Set 안에 연산한 결과를 전부 넣었다.

- 넣을 수 없는경우 -1을 출력한다.

- dp방법도 있는데 나중에 풀어봐야겠다..

 

 

import Foundation

func solution(_ x:Int, _ y:Int, _ n:Int) -> Int {
    var numSet = Set([x])
    var result = 0
    
    while true {
        var addSet = Set<Int>()
        if numSet.contains(y) {
            return result
        }
        for i in numSet {
            if i+n <= y {
                addSet.insert(i+n)
            }
            if i*2 <= y {
                addSet.insert(i*2)
            }
            if i*3 <= y {
                addSet.insert(i*3)
            }
        }
        numSet  = addSet
        result += 1
        if numSet.isEmpty {
            break
        }
    }

    return -1
}

 

 

< DP 풀이>

Array의 크기: y+1, 배열의 원소를 모두 Int의 최댓값으로 선언

시작점 x 의 arr[x] = 0

세가지 연산 x+n , x*2 , x*3 수행후 y보다 같거나 작으면 

  •  x+n <= y : arr[x+n] = min(arr[x]+1, arr[x+n])
  •  x * 2 <= y : arr[x+n] = min(arr[x]+1, arr[x+n])
  •  x * 3 <= y : arr[x+n] = min(arr[x]+1, arr[x+n])

최소 연산횟수를 구해야하므로 min 적용한다.

결과: arr[y] : 만약 세가지 연산으로 arr[y] 의 값을 변경하지 못했다면 arr[y] = Int.max

 

import Foundation

func solution(_ x:Int, _ y:Int, _ n:Int) -> Int {
    var arr = Array(repeating: Int.max, count: y+1)
    arr[x] = 0
    
    for i in x...y where arr[i] != Int.max {
        if i+n <= y {
            arr[i+n] = min(arr[i] + 1, arr[i+n])
        }
        if i*2 <= y {
            arr[i*2] = min(arr[i] + 1, arr[i*2])
        }
        if i*3 <= y {
            arr[i*3] = min(arr[i] + 1, arr[i*3])
        }
    }
    if arr[y] == Int.max {
        return -1
    } else {
        return arr[y]
    }
}

문제 설명

0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.

예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다.

0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.

제한 사항
  • numbers의 길이는 1 이상 100,000 이하입니다.
  • numbers의 원소는 0 이상 1,000 이하입니다.
  • 정답이 너무 클 수 있으니 문자열로 바꾸어 return 합니다.

내가 푼 풀이

- 주어진 정수를 문자열 배열로 만든다.

- 두개의 문자열을 비교했을때 문자가 큰것은 사전순으로 뒤에 온다는 뜻이다.

- 두개의 문자열 s1 , s2 가 s1 +s2 > s2 + s1 은 문자열 두개를 합쳐서 정수로 비교했을때, s1+s2 가 더 큰 값이다.

- 배열내에 모든 문자열을 sorted 함수를 이용해서 정수로 비교했을때 큰경우로 정렬한다.

 

sorted{ return $0 + $1 > $1 + $0} 실행과정

예제 배열 [3, 30, 34, 5, 9]  // print("true \($0) \($1)") , print("false \($1) \($0)")

 

import Foundation

func solution(_ numbers:[Int]) -> String {
    var arr = numbers.map{String($0)}
    var sortedArr = arr.sorted{ return $0 + $1 > $1 + $0 }
    if sortedArr.filter{$0 == "0"}.count == sortedArr.count {
        return "0"
    } else {
        return sortedArr.joined(separator: "")
    }
}

문제 설명

정수로 이루어진 배열 numbers가 있습니다. 배열 의 각 원소들에 대해 자신보다 뒤에 있는 숫자 중에서 자신보다 크면서 가장 가까이 있는 수를 뒷 큰수라고 합니다.
정수 배열 numbers가 매개변수로 주어질 때, 모든 원소에 대한 뒷 큰수들을 차례로 담은 배열을 return 하도록 solution 함수를 완성해주세요. 단, 뒷 큰수가 존재하지 않는 원소는 -1을 담습니다.


제한사항
  • 4 ≤ numbers의 길이 ≤ 1,000,000
    • 1 ≤ numbers[i] ≤ 1,000,000

내가 푼 풀이

- numbers의 길이 최댓값이 1,000,000 이다.

- 단순히 2중 for문으로 인덱스 i 보다 뒤에있는 큰 숫자를 찾게하더니 테스트 20부터 싸그리 시간초과가 떴다.

- 검사할 원소보다 뒤에 있으면서 커야한다.

- 스택을 사용해서 차곡차곡넣으면 가장 윗부분이 검사할 원소와 가장 가까운 원소다.

- numbers 배열을 정순으로 가면 스택을 이용할 그림이 안나와서 역순으로 돌렸다.

- 스택 배열이 비어있으면 추가하고 -1, 매번 검사할때마다 스택을 pop해서 비교한다.

 

 

import Foundation

func solution(_ numbers:[Int]) -> [Int] {
    var arr = [Int]()
    var result = [Int]()
    var num = 0
    
    for i in numbers.reversed() {
        if arr.count == 0 {
            arr.append(i)
            result.append(-1)
        } else {
            num = arr.popLast()!
            while num <= i && arr.count > 0 {
                num = arr.popLast()!
            }
            if num > i {
                result.append(num)
                arr.append(num)
                arr.append(i)
            } else {
                if arr.count == 0 {
                    result.append(-1)
                    arr.append(i)
                }
            }
        }
        
    }

    return result.reversed()
}

프렌즈4블록

블라인드 공채를 통과한 신입 사원 라이언은 신규 게임 개발 업무를 맡게 되었다. 이번에 출시할 게임 제목은 "프렌즈4블록".
같은 모양의 카카오프렌즈 블록이 2×2 형태로 4개가 붙어있을 경우 사라지면서 점수를 얻는 게임이다.

만약 판이 위와 같이 주어질 경우, 라이언이 2×2로 배치된 7개 블록과 콘이 2×2로 배치된 4개 블록이 지워진다. 같은 블록은 여러 2×2에 포함될 수 있으며, 지워지는 조건에 만족하는 2×2 모양이 여러 개 있다면 한꺼번에 지워진다.

블록이 지워진 후에 위에 있는 블록이 아래로 떨어져 빈 공간을 채우게 된다.

만약 빈 공간을 채운 후에 다시 2×2 형태로 같은 모양의 블록이 모이면 다시 지워지고 떨어지고를 반복하게 된다.

위 초기 배치를 문자로 표시하면 아래와 같다.

TTTANT
RRFACC
RRRFCC
TRRRAA
TTMMMF
TMMTTJ

각 문자는 라이언(R), 무지(M), 어피치(A), 프로도(F), 네오(N), 튜브(T), 제이지(J), 콘(C)을 의미한다

입력으로 블록의 첫 배치가 주어졌을 때, 지워지는 블록은 모두 몇 개인지 판단하는 프로그램을 제작하라.

입력 형식

  • 입력으로 판의 높이 m, 폭 n과 판의 배치 정보 board가 들어온다.
  • 2 ≦ n, m ≦ 30
  • board는 길이 n인 문자열 m개의 배열로 주어진다. 블록을 나타내는 문자는 대문자 A에서 Z가 사용된다.

출력 형식

입력으로 주어진 판 정보를 가지고 몇 개의 블록이 지워질지 출력하라.

 

내가 푼 풀이

- 주어진 문자열을 2차원 배열로 만든다.

- 2중 for문으로 원소 하나가 오른쪽, 왼쪽, 오른쪽대각선이 랑 모두같은지 확인한다.

- 모두 같으면 같은 4개의 x,y 위치를 저장한다.

- 이미 저장되있는경우 스킵하고 저장한다

- 저장된 배열을 순회해서 값을 "0"으로 만든다.

- 2차원 배열 보드의 맨 아래부터 빈곳을 확인해서 위에서 아래로 내려준다.

- 이 과정을 반복해서 원소가 같은 4개의 위치가 없는 경우 반복 탈출하고 갯수를 출력한다.

 

 

func solution(_ m:Int, _ n:Int, _ board:[String]) -> Int {
    var gameBoard = [[String]]()
    var removeArr = [[Int]]()
    var isEnd = true
    
    for i in board {    // 주어진 문자열을 2차원배열로 만든다.
        gameBoard.append(i.map{String($0)})
    }
    
    while isEnd {
        var removing = [[Int]]()
        
        for i in 0..<m-1 {  // 2x2를 찾기위해 y의 길이-1까지 탐색
            for j in 0..<n-1 { // 2x2를 찾기위해 y의 길이-1까지 탐색
                var str = gameBoard[i][j]
                if str == "0" { // 이미 소거된경우 패스
                    continue
                }
                var arr = [gameBoard[i][j], gameBoard[i+1][j], gameBoard[i][j+1], gameBoard[i+1][j+1]]
                if arr.filter{ $0 == str }.count == 4 { //  2x2가 모두 같은원소인경우
                    var addSet = [[i, j], [i, j+1], [i+1, j], [i+1, j+1]]
                    for k in addSet {   // x,y위치가 저장되있는 2차원배열에 같은 위치인경우 패스
                        if removing.contains(k) {
                            continue
                        }
                        removing.append(k)
                    }
                }
            }
        }
        
        if removing.isEmpty {   // 더이상 같은 2x2가 없는경우
            break
        } else {
            removeArr += removing   // 2x2의 원소갯수 저장 (x,y 인덱스)
            for i in removing {     // 같은 2x2 위치를 0으로 제거
                gameBoard[i[0]][i[1]] = "0"
            }
        }
        
        for i in 1..<m {    // 중간에 제거된경우 위에서 아래로 내려주기
            for j in 0..<n {
                var xIdx = m-i
                if gameBoard[xIdx][j] == "0" {  
                    var idx = xIdx
                    while gameBoard[xIdx][j] == "0" && xIdx > 0 {  //제거된곳보다 위에있는 제거되지않은 원소 찾기
                        xIdx -= 1
                    }
                    gameBoard[idx][j] = gameBoard[xIdx][j]
                    gameBoard[xIdx][j] = "0"
                }
            }
        }
        
    }

    return removeArr.count
}

문제 설명

파일명 정렬

세 차례의 코딩 테스트와 두 차례의 면접이라는 기나긴 블라인드 공채를 무사히 통과해 카카오에 입사한 무지는 파일 저장소 서버 관리를 맡게 되었다.

저장소 서버에는 프로그램의 과거 버전을 모두 담고 있어, 이름 순으로 정렬된 파일 목록은 보기가 불편했다. 파일을 이름 순으로 정렬하면 나중에 만들어진 ver-10.zip이 ver-9.zip보다 먼저 표시되기 때문이다.

버전 번호 외에도 숫자가 포함된 파일 목록은 여러 면에서 관리하기 불편했다. 예컨대 파일 목록이 ["img12.png", "img10.png", "img2.png", "img1.png"]일 경우, 일반적인 정렬은 ["img1.png", "img10.png", "img12.png", "img2.png"] 순이 되지만, 숫자 순으로 정렬된 ["img1.png", "img2.png", "img10.png", img12.png"] 순이 훨씬 자연스럽다.

무지는 단순한 문자 코드 순이 아닌, 파일명에 포함된 숫자를 반영한 정렬 기능을 저장소 관리 프로그램에 구현하기로 했다.

소스 파일 저장소에 저장된 파일명은 100 글자 이내로, 영문 대소문자, 숫자, 공백(" "), 마침표("."), 빼기 부호("-")만으로 이루어져 있다. 파일명은 영문자로 시작하며, 숫자를 하나 이상 포함하고 있다.

파일명은 크게 HEAD, NUMBER, TAIL의 세 부분으로 구성된다.

  • HEAD는 숫자가 아닌 문자로 이루어져 있으며, 최소한 한 글자 이상이다.
  • NUMBER는 한 글자에서 최대 다섯 글자 사이의 연속된 숫자로 이루어져 있으며, 앞쪽에 0이 올 수 있다. 0부터 99999 사이의 숫자로, 00000이나 0101 등도 가능하다.
  • TAIL은 그 나머지 부분으로, 여기에는 숫자가 다시 나타날 수도 있으며, 아무 글자도 없을 수 있다.

파일명을 세 부분으로 나눈 후, 다음 기준에 따라 파일명을 정렬한다.

  • 파일명은 우선 HEAD 부분을 기준으로 사전 순으로 정렬한다. 이때, 문자열 비교 시 대소문자 구분을 하지 않는다. MUZI와 muzi, MuZi는 정렬 시에 같은 순서로 취급된다.
  • 파일명의 HEAD 부분이 대소문자 차이 외에는 같을 경우, NUMBER의 숫자 순으로 정렬한다. 9 < 10 < 0011 < 012 < 13 < 014 순으로 정렬된다. 숫자 앞의 0은 무시되며, 012와 12는 정렬 시에 같은 같은 값으로 처리된다.
  • 두 파일의 HEAD 부분과, NUMBER의 숫자도 같을 경우, 원래 입력에 주어진 순서를 유지한다. MUZI01.zip과 muzi1.png가 입력으로 들어오면, 정렬 후에도 입력 시 주어진 두 파일의 순서가 바뀌어서는 안 된다.

무지를 도와 파일명 정렬 프로그램을 구현하라.

입력 형식

입력으로 배열 files가 주어진다.

  • files는 1000 개 이하의 파일명을 포함하는 문자열 배열이다.
  • 각 파일명은 100 글자 이하 길이로, 영문 대소문자, 숫자, 공백(" "), 마침표("."), 빼기 부호("-")만으로 이루어져 있다. 파일명은 영문자로 시작하며, 숫자를 하나 이상 포함하고 있다.
  • 중복된 파일명은 없으나, 대소문자나 숫자 앞부분의 0 차이가 있는 경우는 함께 주어질 수 있다. (muzi1.txt, MUZI1.txt, muzi001.txt, muzi1.TXT는 함께 입력으로 주어질 수 있다.)

출력 형식

위 기준에 따라 정렬된 배열을 출력한다.

 

내가 푼 풀이

- TAIL 은 정렬할때 필요가 없으므로, Dictionary(String: [String]]을 선언한다.

- HEAD, NUMBER, 입력순서 세가지를 저장한다.

- 저장할때 HEAD는 첫글자부터 숫자를 만나기전까지, NUMBER은 숫자를 만나서 만나지 않을때까지 순회하면서 저장시킨다.

- 저장된 HEAD, NUMBER 을 문자열로 만들어 Dictionary에 저장한다.

- 3가지 정렬대로 정렬한다. ( 1. HEAD 사전순 2. NUMBER 크기순 3. 입력순서)

- 아예 역순으로 1번 사전순이 같고, 2번 크기가 같으면 입력순서대로, 1번사전순만 같으면 2번 크기순으로, 1번 사전순이 다르면 사전순으로 정렬하게끔 코딩한다.

 

func solution(_ files:[String]) -> [String] {
    var dict = [String: [String]]()
    var result = [String]()
    var idx = 0
    for i in files {
        var arr = i.map{ String($0)}
        var head = [String]()
        var number = [String]()
        
        var headEnd = false
        var numEnd = false
        
        for j in 0..<arr.count {
            if !headEnd {
                if Int(arr[j]) == nil {
                    head.append(arr[j])
                } else {
                    number.append(arr[j])
                    headEnd = true
                }
            } else if !numEnd {
                if Int(arr[j]) != nil {
                    number.append(arr[j])
                } else {
                    break
                }
            }
        }
        dict[String(i)] = [head.joined(separator: ""), number.joined(separator: ""), String(idx)]
        idx += 1
    }
    var sortedDict = dict.sorted{if $0.value[0].uppercased() == $1.value[0].uppercased() {
        if Int($0.value[1]) == Int($1.value[1]) {
            return Int($0.value[2])! < Int($1.value[2])!
        } else {
            return Int($0.value[1])! < Int($1.value[1])!
        }
    } else { return $0.value[0].uppercased() < $1.value[0].uppercased() }}
    for i in sortedDict {
        result.append(i.key)
    }

    return result
}

+ Recent posts