232-aka-pretty-pi 2024. 9. 17. 18:13
namespace lcs_algo {
  using uint64 = unsigned long long;

  constexpr int B = 64;

  std::vector<uint64> bitDP(const std::string& s, const std::string& t, const std::vector<int>& id, int id_cnt) {
    int m = int(t.size());
    int sz = (m + B - 1) / B;
    std::vector bs(id_cnt, std::vector<uint64>(sz));
    for (int i = 0; i < m; ++i) {
      bs[id[t[i]]][i / B] |= 1ULL << (i % B);
    }
    std::vector<uint64> dp(sz);
    for (const char& c : s) {
      const auto& on = bs[id[c]];
      uint64 extra = 1;
      for (int i = 0; i < sz; ++i) {
        uint64 x = on[i] | dp[i];
        uint64 sub = (dp[i] << 1) + extra;
        extra = (x < sub) + (dp[i] >> 63);
        dp[i] = x & (x ^ (x - sub));
      }
      if (m % B != 0) {
        dp[sz - 1] &= (1ULL << (m % B)) - 1;
      }
    }
    return dp;
  }

  int getLcsLength(const std::string& s, const std::string& t) {
    std::vector<int> id(std::numeric_limits<char>::max() + 1, -1);
    int new_id = 0;
    for (const auto& c : s) {
      if (id[c] == -1) {
        id[c] = new_id++;
      }
    }
    for (const auto& c : t) {
      if (id[c] == -1) {
        id[c] = new_id++;
      }
    }
    auto dp = bitDP(s, t, id, new_id);
    int res = 0;
    for (const auto& x : dp) {
      res += __builtin_popcountll(x);
    }
    return res;
  }

  std::string getLcs(const std::string& s, const std::string& t) {
    if (s.empty()) {
      return "";
    }
    std::vector<int> id(std::numeric_limits<char>::max() + 1, -1);
    int new_id = 0;
    for (const auto& c : s) {
      if (id[c] == -1) {
        id[c] = new_id++;
      }
    }
    for (const auto& c : t) {
      if (id[c] == -1) {
        id[c] = new_id++;
      }
    }
    std::string res;
    auto dfs = [&](auto&& dfs, int x, int y, int l, int r) -> void {
      if (y - x == 1) {
        for (int i = l; i < r; ++i) {
          if (s[x] == t[i]) {
            res += s[x];
            break;
          }
        }
        return;
      }
      int m = x + (y - x) / 2;
      auto dp_up = bitDP({s.begin() + x, s.begin() + m}, {t.begin() + l, t.begin() + r}, id, new_id);
      auto dp_down = bitDP({s.rbegin() + s.size() - y, s.rbegin() + s.size() - m}, {t.rbegin() + t.size() - r, t.rbegin() + t.size() - l}, id, new_id);
      int cur_d = 0;
      for (const auto& x : dp_down) {
        cur_d += __builtin_popcountll(x);
      }
      int p = l;
      int max_d = cur_d;
      for (int i = l + 1; i <= r; ++i) {
        cur_d += (dp_up[(i - l - 1) / B] >> ((i - l - 1) % B) & 1);
        cur_d -= (dp_down[(r - i) / B] >> ((r - i) % B) & 1);
        if (cur_d > max_d) {
          max_d = cur_d;
          p = i;
        }
      }
      dfs(dfs, x, m, l, p);
      dfs(dfs, m, y, p, r);
    };
    dfs(dfs, 0, int(s.size()), 0, int(t.size()));
    return res;
  }

  std::string hirschberg(const std::string& s, const std::string& t) {
    if (s.empty()) {
      return "";
    }
    std::string res;
    auto dfs = [&](auto&& dfs, int x, int y, int l, int r) -> void {
      if (y - x == 1) {
        for (int i = l; i < r; ++i) {
          if (s[x] == t[i]) {
            res += s[x];
            break;
          }
        }
        return;
      }
      int m = x + (y - x) / 2;
      std::vector<int> dp_up(r - l + 1);
      for (int row = x; row < m; ++row) {
        auto new_dp = dp_up;
        for (int col = l; col < r; ++col) {
          new_dp[col - l + 1] = std::max(std::max(new_dp[col - l], dp_up[col - l + 1]), dp_up[col - l] + (s[row] == t[col]));
        }
        dp_up = std::move(new_dp);
      }
      std::vector<int> dp_down(r - l + 1);
      for (int row = y - 1; row >= m; --row) {
        auto new_dp = dp_down;
        for (int col = r - 1; col >= l; --col) {
          new_dp[col - l] = std::max(std::max(new_dp[col - l], new_dp[col - l + 1]), dp_down[col - l + 1] + (s[row] == t[col]));
        }
        dp_down = std::move(new_dp);
      }
      int p = -1;
      for (int i = l; i <= r; ++i) {
        if (p == -1 || dp_up[p - l] + dp_down[p - l] < dp_up[i - l] + dp_down[i - l]) {
          p = i;
        }
      }
      dfs(dfs, x, m, l, p);
      dfs(dfs, m, y, p, r);
    };
    dfs(dfs, 0, int(s.size()), 0, int(t.size()));
    return res;
  }
} // namespace lcs_algo