문제 링크
문제 설명
두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.
1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다.
2. words에 있는 단어로만 변환할 수 있습니다.
예를 들어 begin이 "hit", target가 "cog", words가 ["hot","dot","dog","lot","log","cog"]라면 "hit" -> "hot" -> "dot" -> "dog" -> "cog"와 같이 4단계를 거쳐 변환할 수 있습니다.
두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.
제한사항
각 단어는 알파벳 소문자로만 이루어져 있습니다.
각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.
words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.
begin과 target은 같지 않습니다.
변환할 수 없는 경우에는 0를 return 합니다.
문제 이해
이 문제는 한 번에 한 개의 알파벳만 바꿀 수 있고, words
목록 내 단어만 사용할 수 있을 때 최소 변환 횟수를 구하는 문제이다.
문제에서 중요한 단서
가장 짧은 변환 과정 → 최단 경로를 구하는 문제이므로 DFS, BFS, 다익스트라를 시도해 볼 수 있음.
문제 해결 접근 방법
모든 경우를 탐색하는 가장 기본적인 방식인 DFS를 적용해 볼 수 있다.
func solution(_ begin:String, _ target:String, _ words:[String]) -> Int { var minSteps = Int.max var visited = Set<String>() func dfs(_ current: String, _ steps: Int) { if current == target { minSteps = min(minSteps, steps) return } for word in words where !visited.contains(word) { let isOneWordDiff = zip(current, word) .filter { $0 != $1 } .count == 1 if isOneWordDiff { visited.insert(word) dfs(word, steps + 1) visited.remove(word) } } } dfs(begin, 0) return minSteps == Int.max ? 0 : minSteps }
DFS 자체는 최단 경로를 보장하지 않기 때문에 아래와 같은 조건이 추가되어야 한다.
현재 찾은 최단 경로보다 긴 경로는 탐색하지 않음.
이미 더 짧은 경로를 찾았다면, 긴 경로를 탐색할 필요가 없음.
따라서,
minSteps
값을 사용하여 불필요한 탐색을 Pruning 해야 함.
BFS는 DFS보다 효율적으로 불필요한 탐색을 제거할 수 있다.
func solution(_ begin:String, _ target:String, _ words:[String]) -> Int { var queue = [(begin, 0)] var visited = Set<String>() while !queue.isEmpty { let (current, steps) = queue.removeFirst() if current == target { return steps } for word in words { let isOneWordDiff = zip(current, word) .filter { $0 != $1 } .count == 1 if isOneWordDiff && !visited.contains(word) { queue.append((word, steps + 1)) visited.insert(word) } } } return 0 }
대표적인 최단 경로 탐색 알고리즘인 다익스트라를 적용해볼 수도 있다.
func solution(_ begin:String, _ target:String, _ words:[String]) -> Int { var queue = [(begin, 0)] var distance = [String: Int]() for word in words { distance[word] = Int.max } distance[begin] = 0 while !queue.isEmpty { queue.sort { $0.1 < $1.1 } let (current, cost) = queue.removeFirst() if current == target { return cost } for word in words where cost + 1 < (distance[word] ?? Int.max) { let isOneWordDiff = zip(current, word) .filter { $0 != $1 } .count == 1 if isOneWordDiff { distance[word] = cost + 1 queue.append((word, cost + 1)) } } } return 0 }
하지만, 모든 단어 간 변환의 비용이 동일하므로 굳이 다익스트라를 적용할 필요는 없다.