[출처] : https://programmers.co.kr/
두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.
예를 들어 begin이 "hit", target가 "cog", words가 ["hot","dot","dog","lot","log","cog"]라면 "hit" -> "hot" -> "dot" -> "dog" -> "cog"와 같이 4단계를 거쳐 변환할 수 있습니다.
두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.
각 단어에서 한글자씩만 바꾸되, 바꿀 단어는 words에 있어야 한다.
한글자씩 바꿔서 최종적으로 begin
값을 target
값으로 바꾸는 문제다.
이 문제의 접근은 다음과 같다.
결국 단어를 노드로 보고, 간선으로 연결된 노드는 한글자만 바뀐단어라고 생각해서 그래프로 보고 탐색을 해주면 되는 문제였다.
최종 변환 단계를 출력해야되니, 간선의 가중치를 1로 생각해서 인접노드로 갈때 마다 +1 씩해서 변환단계를 세 주면 되는 것이었다.
이 방법은 처음 접근한 방법이다.
check
함수를 만들어서 두 단어가 한글자 차이가 나는지 확인하도록 하였다.
내부 로직은 단순하게 반복문을 글자수만큼 돌려서 비교하는 식으로 했다.(모든 단어의 길이가 같기 때문에 가능함)
bfs
함수는 그래프로 만들고 이를 탐색하면서 시작노드를 [노드번호, 0]
으로 두고, 인접노드로 넘어갈때마다 +1해서 저장하도록 하여 target
단어에 도달했을때 몇단계를 거쳤는지 확인할수 있도록 하였다.
solution
함수는 실제로 실행하는 함수이다.
여기서는 우선 words
안에 target
단어가 없으면 0을 반환해주었다.
그 다음 check
함수를 이용해서 dict
형태에 그래프를 만들었다.
{노드(단어):[인접 노드들]}
이와 같이 만들어주었다.
만든 그래프를 bfs탐색을 해서 몇 단계를 거쳤는지 해당값을 반환해주었다.
from collections import deque
#단어차이가 1인 것을 판별하는 함수
def check(node,word:str)-> bool:
count= 0
for i in range(len(node)):
if node[i] != word[i]:
count+=1
if count >=2:
return False
if count == 1:
return True
else:
return False
#만들어진 트리를 탐색하는 함수
def bfs(start_node:str,graph:dict)-> dict:
visited = dict()
need_visited = deque(list())
#탐색을 위해 처음 값 넣기
need_visited.append([start_node,0])
#반복하면서 탐색
while need_visited:
current_node,current_count = need_visited.popleft()
if current_node not in visited:
visited[current_node] = current_count
if current_node in graph:
for i in graph[current_node]:
need_visited.append([i,current_count+1])
elif current_node in visited and visited[current_node] > current_count:
visited[current_node] = current_count
return visited
#실제로 돌아가는 로직
def solution(begin, target, words):
answer = 0
#target단어가 words안에 없다면
if target not in words:
return 0
#그래프형태로 만들기 - dict형태로 만듦
temp = [begin] + words
graph = {node:list() for node in temp}
temp_queue = deque(temp)
for i in temp:
for j in temp:
if check(i,j) == True:
graph[i].append(j)
result = bfs(begin,graph)
return result[target]
위의 방법은 반복문을 이용해서 미리 그래프를 만들어야 된다는 단점이 있다.
단어 변환과정에서 사용되지 않을 단어도 있을텐데, 이러한 단어들도 전부 그래프에 들어간다는 점이 속도저하를 일으키지 않을까 라고 생각했다.
따라서 그래프를 미리 만들지 않고 bfs탐색시에 만들면서 탐색하는 방법을 고민해보았다.
(문자 체크 함수는 그대로 썼다)
begin
단어와 count=0 이 들어갈것이다이러한 방법은 훨씬 빠르며 노드가 늘어날수록 더 빠를것이다.
미리 그래프를 반복문을 돌면서 만들 필요없이 bfs 탐색시에 하위노드를 만들면서 검사하는 것이다.
시작노드 단어만 넣어두고 check
함수를 이용해서 해당 단어의 인접노드가 될 값들을 찾아서 저장하는 방식이다.
이러면 굳이 미리 그래프를 만들필요가 없어져서 좀 더 빠른 속도를 낼수 있다.
from collections import deque
#단어차이가 1인 것을 판별하는 함수
def check(node,word:str)-> bool:
count= 0
for i in range(len(node)):
if node[i] != word[i]:
count+=1
if count >=2:
return False
if count == 1:
return True
else:
return False
def bfs(start_node,target,graph) -> int:
visited = [False for _ in range(len(graph))]
count = 0
need_visited = deque(list())
need_visited.append([start_node,0])
while need_visited:
current_node,current_count = need_visited.popleft()
#만약 target단어면 current_count를 return
if current_node == target:
count = current_count
break
for i in range(len(graph)):
if check(current_node,graph[i]) == True:
if visited[i] == False:
visited[i] = True
need_visited.append([graph[i],current_count+1])
return count
def solution(begin, target, words):
#target단어가 words안에 없다면
if target not in words:
return 0
result = bfs(begin,target,words)
return result
아래의 두 결과를 보면 속도 차이가 나는 것을 볼 수 있다.
그렇다고 극과 극으로 차이가 나진 않았지만, 만약 그래프의 노드수가 정말 크다면, 이 속도차이는 크게 벌어질 것이라고 생각한다.
첫번째 접근 결과
두번째 접근 결과