ハチの巣型パズル

問題

単語の一覧とハチの巣型(六角形)が並んだ模様に文字が入っていて,単語の一覧をハチの巣の中から選ぶ。

全ての単語をハチの巣から選択した結果残った文字を組み合わせて単語を作る。単語は隣り合う文字を連続させて作る。

例えば上の例では,以下のような3文字が残る。

残った3文字を組み合わせて出来る単語は「パズル」となる。

問題の表現方法(データ構造)

ハチの巣をどうやって表現するか。以下のように各六角形に番号を振って考える。

これを回転して以下のように見ることが出来る。

何となく2次元に見える。これを2次元配列とみなして以下のようにみなす。

このように配置すると,6の廻りにある6つの6角形は

以下のようになる。

ある位置([row][col])の文字の廻りの六角形は以下の6つになる。

[row-1][col-1]
[row-1][col]
[row][col-1]
[row][col+1]
[row+1][col]
[row+1][col+1]

文字の連続を見るには上記6点を見れば良い。

問題の解き方

2次元配列に問題図を読み込むことが出来たら,後は以下のような関数で単語を探す。見つかったらused[ ][ ]に1を設定する。この関数は[row][col]に文字列sを配置できたらYESを返し,配置できなければNOを返す。配置できたときはused[row][col]に1を設定する。

YesNo mark( int row, int col, cstring s)
{
  if( !*s) return YES;
  jap c = getjap(s); c = to_capital_kana(c);
  if( !exist( row, col)) return NO;
  if( word[row][col] != c) return NO;
  if( exist( row-1, col-1) && mark( row-1, col-1, s)) { used[row][col] = 1; return YES;}
  if( exist( row-1, col+0) && mark( row-1, col+0, s)) { used[row][col] = 1; return YES;}

  if( exist( row+0, col-1) && mark( row+0, col-1, s)) { used[row][col] = 1; return YES;}
  if( exist( row+0, col+1) && mark( row+0, col+1, s)) { used[row][col] = 1; return YES;}

  if( exist( row+1, col+0) && mark( row+1, col+0, s)) { used[row][col] = 1; return YES;}
  if( exist( row+1, col+1) && mark( row+1, col+1, s)) { used[row][col] = 1; return YES;}
  return NO;
}

cstringはconst char* で,getjap()は1文字取り出し,sを次の文字を指すように更新し,to_capital_kana()はカナの小文字を大文字にする(例えば「ョ」を「ヨ」に変換する)。

関数exist(int row, int col)は[row][col]に文字が存在すればYESを,存在しなければNOを返す。

YesNo exist( int row, int col)
{
  if( row < 0) return NO;
  if( row >= rows) return NO;
  if( col < 0) return NO;
  if( col >= cols[row]) return NO;
  if( !word[row][col]) return NO;
  return YES;
}

rows は配列の行数。cols[ ]は各行毎にどこまで文字が入っているかを示す。

以下の関数で単語を配列内で探す。

void find( string s)
{
  for( int row=0; row< rows; row++) {
    for( int col=0; col< cols[row]; col++) {
      if( mark( row, col, s.c_str())) return;
    }
  }
  pe( "%s が見つからない\n", s.c_str());
}

全ての単語についてfind()を呼び出す。残った文字(used[ ][ ]==0)を出力する。

問題の入力

上記例題の場合

以下のようなデータを用意する。

リプル
ンコンビ
イタパピツ
#
ズーユト
ルネツ

適当なデリミタ(ここでは'#')で区切り,#が出てくるまではそのまま入力し,#が出ると以降の行は全て1文字ずつずらして配列に入力する。