题目翻译

给定 $n(n\leq 2000)$ 个串,选定 $k$ 个串,求出最大两两 LCP。

题目思路

首先把给定串按字典序排序,保证相邻 LCP 最大。

考虑 dp,$f_{l,r,x}$ 表示选择 $[l,r]$ 中有 $x$ 个串的最大两两 LCP。

类似分治,转移的时候枚举相邻 LCM 最小的点 $m$,一定是在 $m$ 断开最优。转移枚举左右段分别选几个串,加上串的个数乘上 $\operatorname{lcp}(s_m,s_{m+1})$。这个 $\operatorname{lcp}$ 需要预处理。

时间复杂度极小常数 $\mathcal O(n)$。

完整代码

#include <bits/stdc++.h>
using namespace std;
template <typename T>
void chkmx(T &x, T y) { x = max(x, y); }
typedef vector<int> vi;
int n, k;
int lcp[2020];
string s[2020];
vi solve(int l, int r)
{
    if (l == r)
        return vi(2, 0);
    int m = min_element(lcp + l, lcp + r) - lcp;
    vi L = solve(l, m);
    vi R = solve(m + 1, r);
    vi f(r - l + 2, 0);
    for (int l = 0; l < L.size(); l++)
        for (int r = 0; r < R.size(); r++)
            chkmx(f[l + r], L[l] + R[r] + lcp[m] * l * r);
    return f;
}
int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> s[i];
    sort(s + 1, s + n + 1);
    for (int i = 1; i <= n; i++)
    {
        int len = 0;
        while (len < min(s[i].size(), s[i + 1].size()) && s[i][len] == s[i + 1][len])
            len++;
        lcp[i] = len;
    }
    cout << solve(1, n)[k] << endl;
    return 0;
}
最后修改:2024 年 02 月 28 日
v我50吃疯狂星期四