競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 287E

ABC287E

LCPに関する問題.先頭から貪欲に考えるのが基本.Binary Trie に近い.
とりあえず,\(n\) 個は多いので 2つで考える. 2つの文字列 \(s,t \) の LCP は,先頭から貪欲に調べれば求まる.

次に複数の文字列を同時に判定することを考える. これは tree を再帰的に見ていき,類別していくことで実現できる. Tree の葉に向かうにつれて,類が増えていき,細かく分解される.

より正確には,次の様に説明できる.深さ \(d\) とする. \(i, j \in n\) に対して, \(s_{i} \sim s_{j}\)を \(s_{i}\) と \(s_{j}\) の先頭 \(d\) 文字が等しいとき と定める.これは同値関係になる. \(cs_{d} := \{0, 1, \cdots, n-1\} / \sim \) とおく. 任意の \(d\) と 任意の \(c \in cs_{d+1} \)に対して, ある \(c' \in cs_{d}\) が存在して, \(c \subset c'\).
\(c'\) の分解の中に \(c\) が現れるということであり, それは \(i \in c'\) に対して \(s_{i}\) の \(d\) 文字目で類別すればよい.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n, m, k, q;
  cin >> n;
  vector<string> s(n);
  cinv(s);

  vvll v;
  vll ans(n, -1);
  auto f = [&](auto f, ll k = 0) -> void {
    vvll nv;
   
    // k : depth
    for(auto c:v){
     
      vvll tmp(26);
      for(ll j: c){
        if(k < s[j].length()) tmp[s[j][k]-'a'].push_back(j);
        else ans[j] = k;
      }
     
      rep(i,26) if(tmp[i].size()){
        if(tmp[i].size() == 1){
          ll j = tmp[i].front();
          ans[j] = k;
        }else{
          nv.push_back(tmp[i]);
        }
      }
    }

    swap(nv, v);

    if(nv.size()) f(f, k+1);

  };
 
  vll tmp; rep(i,n) tmp.push_back(i);
  v.push_back(tmp);
  f(f);
  rep(i,n){
    cout << ans[i] << endl;
  }

  return 0;
}