C 問題としてはかなりハードな問題であった。
問題概要
個の文字列 のうち、文字列 との編集距離が 1 以下であるものの個数を求めよ。
なお、文字列 と文字列 の編集距離が 1 以下であるとは、以下のいずれかが成り立つことを指す。
- である
- に文字を 1 文字挿入することで に一致する
- から文字を 1 個削除することで に一致する
- の文字を 1 文字変更することで に一致する
制約
- の総和は 以下
考えたこと:まず計算量に注意
計算量が などになってはダメなことに気をつけよう。つまり、文字列 全体に対して for
文を回すような作業を 回やったらダメなのだ。しかし、各文字列 に対して、それぞれ for
文を回すのは大丈夫だ。なぜなら、 の総和を として、 であるからだ。
よって、各文字列 と文字列 の関係性を調べるとき、for
文を回すときは基本的には次のようなループを用いることに気をつけよう。
for (int j = 0; j < S[i].size(); ++j) { }
これの代わりに for (int j = 0; j < T.size(); ++j)
などとしてはならない。
各文字列 に対する判定
それでは、具体的な判定方法を考えていく。一般に、次の値を求めておくことにしよう。
- と をそれぞれ先頭からみていったときに、連続で一致する文字の個数
- と をそれぞれ末尾からみていったときに、連続で一致する文字の個数
これらを求めるのに要する計算量は、基本的に文字列 の方で for 文を 2 回回せばよくて、 となる。
の判定
これが成り立つ条件は次のように記述できる。
S[i].size() == T.size()
であることl == T.size()
であること
に文字を挿入して にできるか
これが成り立つ条件は次のように記述できる。
S[i].size() + 1 == T.size()
であることl + r == S[i].size()
であること
から文字を削除して にできるか
これが成り立つ条件は次のように記述できる。
S[i].size() - 1 == T.size()
であることl + r == T.size()
であること
の文字を 1 文字変更して にできるか
これが成り立つ条件は次のように記述できる。
S[i].size() == T.size()
であることl + r + 1 == T.size()
であること
コード
以上の判定をすると計算量は となる。
#include <bits/stdc++.h> using namespace std; int main() { int N; string T; cin >> N >> T; vector<string> S(N); for (int i = 0; i < N; ++i) cin >> S[i]; vector<int> res; for (int i = 0; i < N; ++i) { // S[i] と T の文字数が 2 文字以上異なっていたらダメ if (abs((int)S[i].size() - (int)T.size()) >= 2) continue; // S[i] の先頭からと末尾から T と一致する文字数を求める int pre = 0, suf = 0; for (pre = 0; pre < min(S[i].size(), T.size()); ++pre) { if (S[i][pre] != T[pre]) break; } for (suf = 0; suf < min(S[i].size(), T.size()); ++suf) { if (S[i][S.size()-1-suf] != T[T.size()-1-suf]) break; } if (pre + suf >= min(S.size(), T.size())) res.push_back(i); else if (S.size() == T.size() && pre + suf + 1 == S.size()) res.push_back(i); } cout << res.size() << endl; for (auto v : res) cout << v+1 << " "; cout << endl; }