C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で解く「単語の梯子(ワードラダー)」問題:ターゲット単語に到達する最短チェーンの長さ

この問題では、辞書と2つの単語「スタート(start)」と「ターゲット(target)」が与えられます。目的は、スタート単語からターゲット単語へと到達する「単語の梯子(ワードラダー)」と呼ばれるチェーンを生成することです。チェーンを構成する各単語は、隣り合う単語とたった1文字だけ異なり、かつすべて辞書に存在しなければなりません。なお、ターゲット単語は辞書に含まれており、すべての単語の長さは同一であるとします。プログラムの出力として、スタートからターゲットまでの最短経路の長さを求めます。

まずは具体例を見ながら、問題の内容を確認しましょう。

入力例

Dictionary = {'HEAL', 'HATE', 'HEAT', 'TEAT', 'THAT', 'WHAT', 'HAIL', 'THAE'}
Start = 'HELL'
Target = 'THAE'

出力例

6

解説

HELL → HEAL → HEAT → TEAT → THAT → THAE

上記の例では、「HELL」を出発点として、各ステップで1文字だけ変更しながら進み、6段階目でターゲットである「THAE」に到達しています。これが最短のチェーンとなり、答えは6です。

解決のアプローチ

この問題を解くには、幅優先探索(BFS)を利用します。辞書内の単語に対して幅優先探索を実行し、直前の単語から1文字だけ異なる単語をレベルごとに順番に見つけていくことで、スタートからターゲットへの梯子(チェーン)を構築できます。

アルゴリズムのポイント

  • BFSはチェーンの長さ(レベル)ごとに探索を進めるため、最初にターゲットへ到達した時点の経路が自動的に最短経路になります。
  • 一度キューに追加した単語は辞書から削除することで、同じ単語を繰り返し探索する無駄を防いでいます。
  • 各文字の位置について「a」~「z」の26文字を試すため、計算量は O(N × L × 26)、つまり約 O(N × L) です(Nは辞書内の単語数、Lは単語の長さ)。

それでは、この解法を実装したC++プログラムを見てみましょう。

実装例

#include <bits/stdc++.h>
using namespace std;
int wordLadder(string start, string target, set<string>& dictionary) {
   if (dictionary.find(target) == dictionary.end())
   return 0;
   int level = 0, wordlength = start.size();
   queue<string> ladder;
   ladder.push(start);
   while (!ladder.empty()) {
      ++level;
      int sizeOfLadder = ladder.size();
      for (int i = 0; i < sizeOfLadder; ++i) {
         string word = ladder.front();
         ladder.pop();
         for (int pos = 0; pos < wordlength; ++pos) {
            char orig_char = word[pos];
            for (char c = 'a'; c <= 'z'; ++c) {
               word[pos] = c;
               if (word == target)
                  return level + 1;
               if (dictionary.find(word) == dictionary.end())
                  continue;
               dictionary.erase(word);
               ladder.push(word);
            }
            word[pos] = orig_char;
         }
      }
   }
   return 0;
}
int main() {
   set<string> dictionary;
   dictionary.insert("heal");
   dictionary.insert("heat");
   dictionary.insert("teat");
   dictionary.insert("that");
   dictionary.insert("what");
   dictionary.insert("thae");
   dictionary.insert("hlle");
   string start = "hell";
   string target = "thae";
   cout<<"Length of shortest chain from '"<<start<<"' to '"<<target<<"' is: "<<wordLadder(start, target, dictionary);
   return 0;
}

出力

Length of shortest chain from 'hell' to 'thae' is: 6

このように、幅優先探索(BFS)を用いることで、1文字ずつ変化させながらターゲット単語へ到達する最短チェーンの長さを効率的に求めることができます。単語変換パズルだけでなく、グラフの最短経路問題全般にも応用できる基本的な手法なので、ぜひマスターしておきましょう。


  1. C++でペアの最大長チェーンを求める方法(動的計画法)

    問題の概要ペアのチェーンが与えられます。各ペアは2つの整数から構成されており、最初の整数は必ず2番目の整数より小さくなっています。チェーンの構築にも同じルールが適用され、ペア (x, y) をペア (p, q) の後に連結できるのは、q < x が成り立つ場合のみです。この問題は、最長増加部分列(LIS)と同じ考え方を応用した動的計画法で効率的に解くことができます。解法の手順は以下のとおりです。与えられたペアを、最初の要素の昇順にソートします。各ペアについて、それ以前のペアの2番目の要素と比較します。arr[i].a > arr[j].b が成り立つ場合、ペア j のチェーンの末尾

  2. 【C++入門】抽象化(Abstraction)の基本とクラスを使った実装方法

    抽象化(Abstraction)とは抽象化とは、外部に対しては必要な情報だけを提供し、背後にある複雑な処理や詳細な実装は隠すという考え方です。プログラミングにおいては、「インターフェースと実装の分離」という原則に基づいて設計を行います。C++における抽象化の実現方法C++ではクラスを使うことで抽象化を実現できます。クラスは、外部からデータを操作するためのpublicメソッドを提供し、それ以外のメンバ変数や内部構造はprivateとして隠蔽します。これにより、利用者はクラスの内部がどのように実装されているかを知らなくても、公開されたメソッドを通じて必要な機能を自由に利用できるようになります。サン