단어 배열에 있는 요소들로 변환해가면서 목표 단어로 변환할 때 까지의 최소 단계를 구하는 문제이다.

boolean[] visited;
int minStep;

int countWord(String begin, String target, String[] words) {
    visited = new boolean[words.length + 1];
    minStep = Integer.MAX_VALUE;

    for (int i = 1; i <= words.length; i++) {
        dfs(begin, target, words, 0, i);
    }

    return minStep == Integer.MAX_VALUE ? 0 : minStep;
}

private void dfs(String nowWord, String target, String[] words, int step, int wordIndex) {
    if (target.equals(nowWord)) {
        minStep = Math.min(step, minStep);
    } else if (wordIndex > words.length) {
        return;
    }

    visited[wordIndex - 1] = true;
    for (int i = 1; i <= words.length; i++) {
        if (visited[i]) continue;
        if (!isCanChange(nowWord, words[i - 1])) continue;

        dfs(words[i - 1], target, words, step + 1, i + 1);
    }
    visited[wordIndex - 1] = false;
}

private boolean isCanChange(String word, String target) {
    int diff = 0;
    for (int i = 0; i < word.length(); i++) {
        if (word.charAt(i) != target.charAt(i)) diff++;
    }
    return diff == 1;
}

단어는 한글자 차이가 나는 단어로만 변환할 수 있다. 단어를 변환할 때마다 마킹하고 다시 모든 요소를 탐색한 뒤 마킹을 해제한다. 주의해야할 부분은 visited를 n + 1로 생성하고 마킹하는 부분이다. 시작 단어도 하나의 노드로 간주한다.

이번엔 그래프와 dfs 재귀를 이용한 코드를 살펴보자.

import java.util.LinkedList;
import java.util.List;

class Solution {

    public int solution(String begin, String target, String[] words) {
        WordGraph wordConverter = new WordGraph(words);
        return wordConverter.step(begin, target);
    }
    
    private class WordGraph {
        int step;
        Word[] words;

        public WordGraph(String[] words) {
            this.words = new Word[words.length + 1];
            for (int i = 1; i <= words.length; i++) {
                this.words[i] = new Word(words[i - 1]);
                for (int j = 1; j < i; j++) {
                    if (!isCanChange(this.words[i].data, this.words[j].data)) continue;

                    addEdge(i, j);
                }
            }
        }

        private void addEdge(int i, int j) {
            Word word = words[i];
            Word word1 = words[j];

            if (!word.adjacents.contains(word1)) {
                word.adjacents.add(word1);
            }
            if (!word1.adjacents.contains(word)) {
                word1.adjacents.add(word);
            }
        }

        private boolean isCanChange(String word, String word1) {
            int diff = 0;
            for (int i = 0; i < word.length(); i++) {
                if (word.charAt(i) != word1.charAt(i)) diff++;
            }
            return diff == 1;
        }

        public int step(String begin, String target) {
            step = Integer.MAX_VALUE;

            Word beginWord = new Word(begin);
            words[0] = beginWord;

            for (int i = 1; i < words.length; i++) {
                if (!isCanChange(words[0].data, words[i].data)) continue;
                addEdge(0, i);
            }

            dfs(beginWord, target, 0);

            return step == Integer.MAX_VALUE ? 0 : step;
        }

        private void dfs(Word word, String target, int step) {
            if (word.data.equals(target)) {
                this.step = Math.min(this.step, step);
                return;
            }
            if (word.marked) return;

            word.marked = true;
            for (Word adjacent : word.adjacents) {
                if (adjacent.marked) continue;
                dfs(adjacent, target, step + 1);
            }
            word.marked = false;
        }

        class Word {
            String data;
            boolean marked;

            List<Word> adjacents;
            public Word(String data) {
                this.data = data;
                adjacents = new LinkedList<>();
            }

            public boolean canChange(String word) {
                int diff = 0;
                for (int i = 0; i < data.length(); i++) {
                    if (data.charAt(i) != word.charAt(i)) diff++;
                }

                return diff == 1;
            }
        }
    }
}

각 단어를 노드로 그래프를 생성한다. 이번에는 탐색을 편하게 하기 위해, 그래프를 생성하면서 각 노드가 변환될 수 있는 노드를 미리 연결한다.

step을 호출하여 dfs를 수행하면 위의 그림처럼 길을 찾는 것과 비슷한 과정으로 진행된다. [hot, dot, dog, lot, log, cog] 의 단어배열에서 hit 단어로 시작하여 cog까지 가장 빠른 변환 단계는 빨간색 혹은 파란색 루트를 거치는 4단계인 것을 알 수 있다.