▶문제설명
코딩테스트 연습 > DFS/BFS > 단어 변환
▶ 설계
DFS 탐색으로 문제를 해결해보자.
한 번에 한 개의 알파벳만 바꾸어서 word에 있는 단어 중 하나로 변환할 수 있는지 모든 경우를 탐색해보면 되는 문제이다.
복잡한 것 없이 문제에서 요구하는데로 재귀 함수만 잘 구현하면 간단히 해결될 것으로 보인다.
▶ 구현
우선 재귀함수의 매개변수와 리턴타입을 정해보자.
1 2 3 4 5 | int findWord(string nowWord, const string& targetWord, const vector<string>& words, int cnt){ int answer = 987654321; return answer; } | cs |
cnt변수는 재귀 함수의 깊이를 나타내는 변수이다.
정답을 찾아가는 경로 중 최소값을 찾아야 하므로 answer를 987654321이라는 값으로 초기화해주었다.
answer를 리턴하기 전에 answer에는 최소 값이 들어있게 되거나 targetWord로 갈 수 있는 방법이 없을 경우엔 987654321이 그대로 들어있게 될 것이다.
재귀 함수의 종료 조건식을 세워주자.
1 2 3 4 5 6 7 8 9 | int findWord(string nowWord, const string& targetWord, const vector<string>& words, int cnt){ int answer = 987654321; if(nowWord.compare(targetWord) == 0){ return cnt; } return answer; } | cs |
nowWord가 targetWord와 같아졌을 때 재귀 함수의 깊이를 반환하도록 구현했다.
이제 핵심이라고 할 수 있는 가능한 모든 경우를 탐색하며 재귀 호출하는 부분을 구현해야한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | int findWord(string nowWord, const string& targetWord, const vector<string>& words, int cnt){ int answer = 987654321; if(nowWord.compare(targetWord) == 0){ return cnt; } for(int i=0; i<nowWord.length(); i++){ if(nowWord[i] == targetWord[i]) continue; string nextWord(nowWord); for(int j=0; j<26 ;j++){ nextWord[i]='a'+j; for(int k=0; k<words.size(); k++){ if(words[k].compare(nextWord) == 0){ int ret = findWord(nextWord, targetWord, words, cnt+1); answer = min(answer,ret); } } } } return answer; } | cs |
nowWord의 인덱스를 순회하면서 targetWord와 같은 인덱스에 있는 알파벳이 동일할 경우에는 다른 알파벳으로 교체해볼 필요가 없으므로 continue문으로 무시하도록 구현했다.
그렇지 않은 경우에는 해당 인덱스의 알파벳을 26개의 알파벳으로 일일이 변경한 뒤 words에서 일치하는 단어가 있는지 확인하고 있다.
일치하는 단어가 있는 경우에는 함수를 재귀적으로 호출하게 되는데, 그 단어를 첫 번째 인자로 넘겨주고 깊이를 나타내는 cnt를 1 증가시켜서 넘겨준다.
첫 번째 인자로 nextWord를 넘겨주든 words[k]를 넘겨주든 똑같은 결과를 얻을 수 있다.
그리고 answer의 값보다 재귀 호출로 반환 받은 값이 더 작은 경우에 answer의 값을 갱신하도록 구현했다.
이렇게 하고 프로그램을 돌리면 무한루프에 빠지게 된다. 그 이유는 조금만 생각해보면 알 수 있다.
이미 선택된 단어를 죽을 때까지 계속해서 선택할 수 있기 때문이다. 이 문제를 해결해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | bool usedWord[50]; int findWord(string nowWord, const string& targetWord, const vector<string>& words, int cnt){ int answer = 987654321; if(nowWord.compare(targetWord) == 0){ return cnt; } for(int i=0; i<nowWord.length(); i++){ if(nowWord[i] == targetWord[i]) continue; string nextWord(nowWord); for(int j=0; j<26 ;j++){ nextWord[i]='a'+j; for(int k=0; k<words.size(); k++){ if(usedWord[k] == false && words[k].compare(nextWord) == 0){ usedWord[k] = true; int ret = findWord(nextWord, targetWord, words, cnt+1); answer = min(answer,ret); usedWord[k] = false; } } } } return answer; } | cs |
이렇게 조금만 수정해주면 word[k]가 이미 사용된 단어인 경우에는 무시하게 된다.
이런 브루트 포스 문제는 재귀호출을 해준 뒤에 다시 원래의 상태로 돌려주는 부분이 있어야 모든 경우를 탐색할 수 있음에 유의하자.
▶ 결과
▶Solution
'■ 알고리즘 문제 풀이 > 프로그래머스' 카테고리의 다른 글
[프로그래머스] 네트워크 (0) | 2019.12.18 |
---|---|
[프로그래머스] 카펫 (0) | 2019.12.17 |
[프로그래머스] 소수 찾기 (0) | 2019.12.14 |
[프로그래머스] 다리를 지나는 트럭 (0) | 2019.12.13 |
[프로그래머스] 여행경로 (0) | 2019.12.12 |