公式

C - Fishbones 解説 by sheyasutaka


実装方針

\(S_j\)\(i\) 文字目について,以下を高速に判定することを考えます.

  • 長さ \(A_i\) 文字で,\(B_i\) 文字目が \(S_j[i]\) と一致するような文字列が \(S_1, \cdots, S_M\) のなかに存在するか?

これを愚直な実装から高速化することは困難に見えます.そこで,前もって必要な値を求めておくことを考えます.

\(\mathrm{memo}[len][place][c]\) を,「長さ \(len\) 文字で,\(place\) 文字目が \(c\) と一致するような文字列が \(S_1, \cdots, S_M\) のなかに存在する」かどうかを表すべき値とします.これらを False で初期化し,以下の前処理を行います.

  • \(S_j\)\(i\) 文字目について,その文字が \(c\) のとき,\(\mathrm{memo}[|S_j|][i][c]\) を True にする.

この前処理は \(O(N^2_{max} \sigma + N_{max}M)\) 時間で動きます(\(N_{max} := \max(|S_1|, \cdots, |S_M|)\).また,\(\sigma\) は文字の種類数). この前処理が終わった時点で,\(\mathrm{memo}\) の各値は正しい値をもちます.

判定は各位置について \(O(1)\) 時間で行えるようになったので,全体で \(O(N^2_{max} \sigma + N_{max}M)\) 時間で動き,十分高速です.

実装例 (C++)

C++ での実装例を以下に示します.

なお,この実装例は英小文字 (a-z) の文字コードが連続であることを前提にしています. そのため,AtCoder のジャッジサーバをはじめとする主流の環境では意図通りに動作しますが,必ずしも手元の環境で意図通りに動くとは限りません.

#include <iostream>
using std::cin;
using std::cout;
#include <vector>
using std::vector;
using std::pair;
using std::make_pair;
using std::min;
using std::max;
#include <string>
using std::string;

typedef long long int ll;

const int MAX_N = 10;
const int SIGMA = 26;

int n, m;
vector<int> a, b;
vector<string> s;

void solve () {
	// has[i][j][k] == 1 iff there exists s such that |s| == i, s[j] == ('a' + k)
	int has[MAX_N + 1][MAX_N][SIGMA];
	// init with 0
	for (int i = 0; i <= MAX_N; i++) {
		for (int j = 0; j < MAX_N; j++) {
			for (int k = 0; k < SIGMA; k++) {
				has[i][j][k] = 0;
			}
		}
	}
	// set 1
	for (int i = 0; i < m; i++) {
		int sz = s[i].size();
		for (int j = 0; j < sz; j++) {
			has[sz][j][s[i][j] - 'a'] = 1;
		}
	}

	vector<int> isyes(m, 0);
	// answer for each s
	for (int i = 0; i < m; i++) {
		int sz = s[i].size();
		if (sz != n) continue;

		bool isok = true;
		for (int j = 0; j < n; j++) {
			if (has[a[j]][b[j] - 1][s[i][j] - 'a'] == 0) isok = false;
		}
		if (isok) {
			isyes[i] = 1;
		}
	}

	// output
	for (int i = 0; i < m; i++) {
		cout << (isyes[i] ? "Yes" : "No") << "\n";
	}
}

int main (void) {
	std::cin.tie(nullptr);
	std::ios_base::sync_with_stdio(false);

	cin >> n;
	a.resize(n);
	b.resize(n);
	for (ll i = 0; i < n; i++) {
		cin >> a[i] >> b[i];
	}

	cin >> m;
	s.resize(m);
	for (ll i = 0; i < m; i++) {
		cin >> s[i];
	}


	solve();

	return 0;
}

投稿日時:
最終更新: