Official

G - 蛇行する文字列 / Find the Snaky Editorial by Nyaan


この問題は全探索(力任せ探索)を上手く実装すれば解くことが出来ます。
具体的には、次の手順でマスを 1 つずつ決め打つ全探索を行えばよいです。

  • マスを 1 つ選ぶ。マスが 1 文字目として条件を満たしているマスに関して、それを \(1\) 番目のマスと決め打って次の探索に進む。
    • \(1\) 番目に選んだマスの周囲 \(8\) マスを全探索する。条件を見たすマスに関して、それを \(2\) 番目のマスと決め打って次の探索に進む。
      • \(\vdots\)
        • \(N-1\) 番目に選んだマスの周囲 \(8\) マスを全探索する。条件を見たすマスが存在した場合は答えを発見したことになるので Yes を出力して探索を終了する。
  • 探索で何も発見できなかった場合は No を出力する。

計算量は \(1\) 文字目の選び方が \(H \times W \leq 100\) 通りでそれ以外の選び方が周囲 \(8\) マスの分の \(8\) 通りなので、探索は高々 \(100 \times 8^{N-1} \times N = 2048000\) ステップしか発生せず十分高速です。

  • 実装例(C++)
#include <cassert>
#include <iostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
using namespace std;

int H, W, N;
string S;
vector<string> G;
set<pair<int, int>> st;

void dfs(int i, int h, int w) {
  assert(0 <= h and h < H);
  assert(0 <= w and w < W);
  assert(0 <= i and i < N);
  assert(G[h][w] == S[i]);

  if (i == N - 1) {
    cout << "Yes" << endl;
    exit(0);
  }

  st.emplace(h, w);
  for (int dh = -1; dh <= 1; dh++) {
    for (int dw = -1; dw <= 1; dw++) {
      if (dh == 0 and dw == 0) continue;
      int nh = h + dh;
      int nw = w + dw;
      if (!(0 <= nh and nh < H)) continue;
      if (!(0 <= nw and nw < W)) continue;
      if (st.count({nh, nw})) continue;
      if (G[nh][nw] != S[i + 1]) continue;
      dfs(i + 1, nh, nw);
    }
  }
  st.erase({h, w});
}

int main() {
  cin >> H >> W;
  G.resize(H);
  for (auto& g : G) cin >> g;
  cin >> N >> S;

  for (int h = 0; h < H; h++) {
    for (int w = 0; w < W; w++) {
      if (G[h][w] == S[0]) dfs(0, h, w);
    }
  }
  cout << "No" << endl;
}

posted:
last update: