[프로그래머스/CPP/JS] 단어 변환

연성·2020년 11월 14일
0

코딩테스트

목록 보기
139/261
post-custom-banner

[프로그래머스] 단어 변환

1. 문제

두 개의 단어 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 함수를 작성해주세요.

2. 제한 사항

  • 각 단어는 알파벳 소문자로만 이루어져 있습니다.
  • 각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.
  • words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.
  • begin과 target은 같지 않습니다.
  • 변환할 수 없는 경우에는 0를 return 합니다.

3. 풀이

  • begin에서 갈 수 있는 index를 모두 저장한다.
  • 각 단어끼리 연결할 수 있는지 확인하여 연결 그래프를 만든다.
  • begin에서 갈 수 있는 index를 시작점으로 BFS를 하여 최단 거리를 구한다.

4. 처음 코드와 달라진 점

  • begin이 words 배열 안의 문자열인지 알고 문제를 풀었다.
  • fill_graph에서 begin에 연결된 노드를 찾는 코드를 넣을까 하다가 함수 하나에서 두 개의 동작을 하는게 별로 안 좋을 것 같아서 함수를 분리했다.
  • 시작점에서 바로 옮길 수 있음에도 한 번 거쳐서 옮기는 경우가 생겼다.
    target인지 아닌지 확인하는 코드의 위치가 문제였다.
  • 비교하는 코드를 수정하여 완성했다.
  • 저 비교하는 코드 때문에 생긴 문제를 잘못 이해해서 쓸데 없는 min함수가 들어갔다. 수정해서 뺐다.

5. 의문점

  • target이 배열 안에 있음에도 연결되지 않는 경우는 0을 출력해야 하지 않나 싶었는데 사람들 얘기가 신경 안 써도 통과하는데 문제 없다고 해서 일단 제출했는데 통과되었다.
  • 그래도 연결되어 있지 않은 경우에 0을 출력할 수 있게 코드를 짰다.

6. 코드

#include <iostream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

vector<int> graph[50];
vector<int> right_next_node;
bool visited[50];

bool can_convert(string w1, string w2){
    int len = w1.size();
    int count = 0;
    for(int i=0; i<len; i++){
        if(w1[i] == w2[i]) count++;
    }
    return len-1 == count ? true : false;
}

void fill_graph(string begin, vector<string> words){
    for(int i = 0; i<words.size()-1; i++){
        string current_word = words[i];
        for(int j = i+1; j<words.size(); j++){
            string word_to_compare = words[j];
            
            if(can_convert(current_word, word_to_compare)){
                graph[i].push_back(j);
                graph[j].push_back(i);
            }
        }
    }
}

void find_right_next_node(string s, vector<string> words){
    for(int i=0; i<words.size(); i++){
        string word_to_compare = words[i];
        if(can_convert(s, word_to_compare)) right_next_node.push_back(i);
    }
}

int bfs(int target){
    if(target == -1) return 0;
    queue<pair<int, int>> q;

    for(int i=0; i<right_next_node.size(); i++){
        int next_node = right_next_node[i];
        visited[next_node] = true;
        q.push({next_node, 1});
    }
    
    while(!q.empty()){
        int current_node = q.front().first;
        int current_depth = q.front().second;
        if(current_node == target) return current_depth;
        q.pop();
        for(int i=0; i<graph[current_node].size(); i++){
            int next_node = graph[current_node][i];
            if(visited[next_node]) continue;
            visited[next_node] = true;
            q.push({next_node, current_depth+1});
        }
    }
    return 0;
}

int solution(string begin, string target, vector<string> words) {
    fill_graph(begin, words);
    find_right_next_node(begin, words);
    
    int begin_index, target_index=-1;
    for(int i=0; i<words.size(); i++){
        if(words[i]==begin) begin_index = i;
        else if(words[i] == target) target_index = i;
    }

    int answer = bfs(target_index);
    return answer;
}

➕ 21.09.01 추가

7. 풀이 2

  • begin에서 갈 수 있는 인덱스를 따로 저장하지 않고 그 때 그 때 words 배열의 원소로 갈 수 있는지 확인하는 식으로 수정했다.
  • (word, depth) 쌍으로 queue를 만든다.
  • queue에 (begin, 0)을 삽입한다.
  • queue에서 원소를 꺼내서 한 글자만 바꿔서 변경할 수 있는 words 배열의 원소를 찾는다.
  • 한 글자만 바꿔서 변경할 수 있는 단어를 찾으면 큐에 추가하고 방문처리를 한다.
  • queue에서 꺼낸 원소가 target과 같다면 해당 depth를 answer에 저장하고 반복문을 종료한다.

8. 코드 2

#include <string>
#include <vector>
#include <queue>

using namespace std;

int solution(string begin, string target, vector<string> words) {
    int answer = 0;
    bool visited[50] = {0, };
    
    queue<pair<string, int>> q;
    q.push({begin, 0});
    
    while (!q.empty()) {
        string word = q.front().first;
        int depth = q.front().second;
        q.pop();
        
        if (word == target) {
            answer = depth;
            break;
        }
        
        for (int i = 0; i < words.size(); ++i) {
            if (visited[i]) continue;
            int count = 0;
            
            for (int j = 0; j < words[i].size(); ++j) {
                if (word[j] == words[i][j]) count++;
            }
            
            if (count == words[i].size() - 1) {
                q.push({words[i], depth + 1});
                visited[i] = true;
            }
        }
        
    }
    
    return answer;
}

9. 코드(JS)

function solution(begin, target, words) {
    let answer = 10e9;
    let visited = Array(words.length).fill(false);
    
    function dfs(word, cnt) {
        if (word === target) {
            answer = Math.min(answer, cnt);
        }
        else {
            for (let i = 0; i < words.length; i++) {
                if (visited[i]) continue;
                let match = 0;
                for (let j = 0; j < words[i].length; j++) {
                    if (words[i][j] === word[j]) match++;
                }
                if (match === word.length - 1) {
                    visited[i] = true;
                    dfs(words[i], cnt + 1);
                    visited[i] = false;
                }
            }
        }
    }
    
    dfs(begin, 0)
    answer = answer === 10e9 ? 0 : answer;
    return answer;
}
post-custom-banner

0개의 댓글