公式

G - Wildcards 解説 by leaf1415


文字列 \(T\)\(L\) 文字のうち ? にする \(K\) 個の位置を固定し、その位置をそれぞれ \(p_1, p_2, \ldots, p_K\) 文字目とします。 このとき、残りの \(L-K\) 文字をどのように選ぶのが最適でしょうか?

\(i = 1, 2, \ldots, N\) について、「\(S_i\)\(T\) から作れる」ことは 「\(S_i\) のうち \(p_1, p_2, \ldots, p_K\) 文字目以外の \(L-K\) 文字が \(T\) と一致している」 ことと同値です。 さらにそれは「\(S_i\)\(p_1, p_2, \ldots, p_K\) 文字目を ? に置き換えて得られる文字列 \(S'_i\)\(T\) と一致している」 と言い換えられます。

したがって、\(T\) から作れる \(S_i\) の個数を最大化するには、\(S_1, S_2, \ldots, S_N\) のそれぞれの \(p_1, p_2, \ldots, p_K\) 文字目を ? に置き換えて得られる \(N\) 個の文字列 \(S'_1, S'_2, \ldots, S'_N\) を列挙し、その中に最も多く登場する文字列を \(T\) として選べば良いです。 最も多く登場する文字列を特定するために、各文字列の登場回数を数え上げるのには連想配列を用いることができます。

以上で、文字列 \(T\)\(L\) 文字のうち ? にする \(K\) 個の位置を固定した場合の最適解を得ることができました。 本問題を解くには、? にする \(K\) 個の位置の選び方(全 \(\binom{L}{K}\) 通り)全てを試せば良いです。

以下に C++ 言語による正解例を記載します。

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int n, l, k;
string s[1005];

int main(void)
{
  cin >> n >> l >> k;
  for(int i = 1; i <= n; i++) cin >> s[i];
  
  int L = 1<<l, ans = 0;
  for(int i = 0; i < L; i++){
    vector<int> vec;
    for(int j = 0; j < l; j++) if(i & (1<<j)) vec.push_back(j);
    if((int)vec.size() != k) continue;
    
    map<string, int> mp;
    for(int j = 1; j <= n; j++){
      string str = s[j];
      for(auto x : vec) str[x] = '?';
      mp[str]++;
    }
    for(auto p : mp) ans = max(ans, p.second);
  }
  cout << ans << endl;
  
  return 0;
}

投稿日時:
最終更新: