[고득점Kit][DFS][BFS] 단어 변환 ⭐⭐⭐
카테고리: Programmers
태그: BFS Coding Test DFS Algorithm
[DFS][BFS] 단어 변환
난이도 ⭐⭐⭐
문제
BFS, DFS 두 풀이로 풀어본 문제다. 맞아서 기분 좋다. 🥰
BFS 로 푼 풀이 ⭕
#include <string>
#include <vector>
#include <queue>
using namespace std;
int differentCount(string str, string target)
{
int differentCount = 0;
for (int i = 0; i < str.length(); i++)
{
if (str[i] != target[i])
differentCount++;
}
return differentCount;
}
struct compare
{
bool operator() (pair<string, int> a, pair<string, int> b)
{
return a.second > b.second;
}
};
int solution(string begin, string target, vector<string> words) {
int answer = 0;
priority_queue<pair<string, int>, vector<pair<string, int>>, compare> waiting_queue;
vector<bool> pushedStr(words.size());
waiting_queue.push(make_pair(begin, differentCount(begin, target)));
while (!waiting_queue.empty())
{
begin = waiting_queue.top().first;
waiting_queue.pop();
if (begin == target) break;
for (int i = 0; i < words.size(); i++)
{
if (!pushedStr[i] && differentCount(begin, words[i]) == 1)
{
waiting_queue.push(make_pair(words[i], differentCount(words[i], target)));
pushedStr[i] = true;
}
}
answer++;
}
if (begin != target) answer = 0;
return answer;
}
begin
은 처음엔 “hit”으로 시작하지만 기존begin
에서 한 글자 수정하면 바꿀 수 있는 단어로 계속해서 업데이트</u> 된다.begin
의 변화 과정 “hit” 👉 “hot” (1) 👉 “dot” (2) 👉 “dog” (3) 👉 “cog” (4)- 변화할 때마다
answer
1 증가.
- 변화할 때마다
begin
이target
과 같아질 때 위 작업을 종료한다.
- 새로운
begin
을 결정하기 위하여- 현재
begin
기준에서 한 글자 수정하면 바꿀 수 있는 단어들을 모조리 우선순위 큐에 넣는다. 👉 BFS 방식 - 우선순위 큐는
target
과 다른 글자 수가 가장 적은 수가 우선적으로 나오게 되는 Min Heap 으로 만든다. 즉,target
에 가장 가까운 단어가 나오도록 함.- 새로운
begin
을 정할 때 우선순위큐에서 pop 시키면 현재 후보들 중 가장target
에 가까운 단어가 나오도록 한다.- 이렇게 우선순위큐를 사용하니 최소 횟수로 변화시키는게 가능해진다.
- 새로운
- 현재
int differentCount(string str, string target)
{
int differentCount = 0;
for (int i = 0; i < str.length(); i++)
{
if (str[i] != target[i])
differentCount++;
}
return differentCount;
}
struct compare
{
bool operator() (pair<string, int> a, pair<string, int> b)
{
return a.second > b.second;
}
};
int solution(string begin, string target, vector<string> words) {
priority_queue<pair<string, int>, vector<pair<string, int>>, compare> waiting_queue;
waiting_queue.push(make_pair(begin, differentCount(begin, target)));
vector<bool> pushedStr(words.size());
- int differentCount(string str, string target) 함수
- 인수로 넘긴 두 문자열의 서로 다른 문자 수를 리턴해준다.
- “hit” 과 “hot” 을 넘기면 1 이 나올 것이다.
- 인수로 넘긴 두 문자열의 서로 다른 문자 수를 리턴해준다.
waiting_queue
우선순위 큐pair<string, int>
타입을 원소로 한다.string
(first) 👉 단어. 우선순위 큐에 들어가 후보로 있는 단어.int
(second) 👉target
과 다른 글자 수. 이 수가 적을 수록target
과 가까운 수이다. first로target
을 가진 원소의 경우 second 를 0 의 값을 가질 것이다.- 우선순위 기준은 이
second
이 가장 작은 값이 튀어나오게 한다. Min Heap. (오름차순 힙 정렬)
- 우선순위 기준은 이
- 먼저 시작전, 처음 시작 단어
begin
를 우선순위 큐에 넣어준다.
-
pushedStr
벡터로 이미 큐에 한번 들어갔던 경험이 있는 단어인지를 체크한다. 중복으로 큐에 또 넣지 않기 위하여.while (!waiting_queue.empty()) { begin = waiting_queue.top().first; waiting_queue.pop(); if (begin == target) break; for (int i = 0; i < words.size(); i++) { if (!pushedStr[i] && differentCount(begin, words[i]) == 1) { waiting_queue.push(make_pair(words[i], differentCount(words[i], target))); pushedStr[i] = true; } } answer++; } if (begin != target) answer = 0; return answer;
우선순위큐가 빌 때까지 진행한다.
- 우선순위 큐에 삽입되는 기준
- 기존에 큐에 들어간적이 없어야 하며
- 큐에서 하나 뽑아오는 반복마다
words
를 처음부터 다시 살피기 때문에 방문 경력을 체크해두어야 한다.
- 큐에서 하나 뽑아오는 반복마다
begin
에서 1 글자만 바꾸면 수정 가능한 단어여야 한다.- differentCount(begin, words[i]) == 1
- 기존에 큐에 들어간적이 없어야 하며
-
우선순위 큐가 다 빌 때까지, 즉 더 이상 단어가 들어갈 수 없어서 작어빙 종료 되었을 때까지도 (begin != target)인 상태면 애초에
words
에 없었던지 바꿀 수 없었던 단어였던지 한 단어이므로 0 을 리턴한다.- 우선순위큐 [“hit”]
- “hit” 👉 우선순위큐 [“hot”]
- “hot” 👉 우선순위큐 [“dot”, “lot”] : 둘 다 cog 와 글자 수 2 차이나므로 우선순위는 같음
- “dot” 👉 우선순위큐 [“lot”, “dog”] : dog 가 lot 보다 cog 에 가까우니 다음엔 dog 가 나올 것이다.
- “dog” 👉 우선순위큐 [“lot”, “cog”, “log”] : 다음엔 당연히 cog 와 일치하는 “cog”가 나올 것이다.
- “cog” target과 같으니 종료.
- 우선순위 큐에 삽입되는 기준
DFS 로 푼 풀이 ⭕
#include <string>
#include <vector>
using namespace std;
bool canChangeWord(string a, string b)
{
int differentCount = 0;
for (int i = 0; i < a.length(); i++)
{
if (a[i] != b[i])
differentCount++;
}
if (differentCount == 1) return true;
else return false;
}
void DFS(vector<string> words, vector<bool> visited, int& answer, string target, string begin, int depth)
{
if (begin == target)
{
answer = min(answer, depth);
return;
}
for (int i = 0; i < words.size(); i++)
{
if (!visited[i] && canChangeWord(begin, words[i]))
{
visited[i] = true;
DFS(words, visited, answer, target, words[i], depth + 1);
}
}
}
int solution(string begin, string target, vector<string> words) {
int answer = 100;
vector<bool> visited(words.size());
DFS(words, visited, answer, target, begin, 0);
if(answer == 100) return 0;
else return answer;
}
DFS 방식
1 글자 차이 밖에 안나서 바꿀 수 있는 단어라면 냅다 깊게 들어간다. 대신 이런식으로 끝까지 들어갔다 나오므로 매 순회 경로마다 depth
재귀 길이를 기록하고, 해당 재귀에서 begin == target
드디어 성공적으로 바뀌었을 때마다 그때의 depth
와 현재까지 기록한 answer
中 더 작은 것을 저장하고 리턴하면 된다. 이러면 target
과 일치하게 된 성공적인 결과에 도달하기까지 가장 짧았던 재귀 길이가 최종적으로 answer
에 저장되게 된다.
bool canChangeWord(string a, string b)
{
int differentCount = 0;
for (int i = 0; i < a.length(); i++)
{
if (a[i] != b[i])
differentCount++;
}
if (differentCount == 1) return true;
else return false;
}
1 글자 차이라서 바꿀 수 있다면 true
리턴. 아니라면 false
리턴 해주는 함수. 즉 a
에서 b
로 변경이 가능한지를 알려주는 함수다.
void DFS(vector<string> words, vector<bool> visited, int& answer, string target, string begin, int depth)
{
if (begin == target)
{
answer = min(answer, depth);
return;
}
for (int i = 0; i < words.size(); i++)
{
if (!visited[i] && canChangeWord(begin, words[i]))
{
visited[i] = true;
DFS(words, visited, answer, target, words[i], depth + 1);
}
}
}
- 매개 변수
answer
는&
참조 타입이어야 한다.- DFS 이기 때문에 함수가 여러개 호출이 된다. 참조가 아니라면 함수 마다
answer
는 동일한 메모리가 아닌 별개의 메모리로서 그저 값만 복사했을 뿐인 것이다. (Call by Value) - 종료 조건마다 기존
answer
와depth
중 더 작은 것으로answer
를 업데이트 해야 하기 때문에 모든 재귀 호출 함수들에게answer
메모리가 동일하게 공유될 수 있어야 한다. 따라서 참조로 선언.
- DFS 이기 때문에 함수가 여러개 호출이 된다. 참조가 아니라면 함수 마다
visited
는 참조 타입이면 안된다.- 각 단어들의 방문 상태는 각 재귀 호출 함수(방문 경로)마다 다르기 때문이다.
- 아무 생각없이 참조로 했다가 애먹었다..
&
하나 차이로 결과가 이리 달라지다니 ㅠ
int answer = 100;
성공적으로 target
로 변경 완료한 재귀 호출 함수들에서 answer
와 depth
를 비교하고 그 중 작은 값을 넣을 것이기 때문에 answer
의 시작값은 0 이면 안된다. 절대 업데이트 안될 것이다. 따라서 words
의 최대 길이인, 이 경우의 재귀 최대 깊이인 100 으로 초기화 해주고 넘겨주었다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기