[ARC058F] Iroha Loves Strings

cxqghzj發表於2024-03-29

題意

給定 \(n\) 個字串 \(s_1, s_2, ..., s_n\)

你需要在其中選擇一些字串,按照順序拼接。

在所有生成的長度為 \(k\) 的字串中,選擇字典序最小的一個。

\(n \le 2000, k \le 10 ^ 4, \sum |s_i| \le 10 ^ 6\)

Sol

考慮一個樸素的 dp。

\(f_{i, j}\) 表示前 \(i\) 個字串,放到了前 \(j\) 個位置的最小的字串。

這個 dp 的複雜度為 \(nk \times \sum |s_i|\)

不難注意到一個事情,如果當前放的字串 \(s_i\) 已經不是最優的字典序了,那麼無論後面怎麼放,此處放 \(s_i\) 都不可能成為最優方案。

考慮想的暴力一點:

\(f_{i, j, k}\) 表示用第 \(j\) 個字串的第 \(k\) 位填在第 \(i\) 位,是否可以為當前字典序最小的方案,且合法。

先考慮簡單的合法,每次新放一個字串都需要判斷放當前字串是否可以組成長度為 \(k\) 的答案。

\(g_{i, j}\) 表示用第 \(j\) 個字串填完前 \(i\) 位是否能合法。

顯然:

\[\forall k \in [j + 1, n], g_{i, j} = g_{i, j} \vee g_{i + len_j, k} \]

\(g_{i, j}\) 顯然可以在 \(O(nk)\) 的時間求出。

考慮當前的最小字元。

注意到當前可以填的最小字元 \(s_{j, k}\) 滿足:

\[\left\{ \begin{aligned} & \min_{j, k} s_{j, k + 1} , & & [f_{i - 1, j, k} = 1] \\ & \min_{j} s_{j, 1} , & & [\exists k, k < j, f_{i - 1, k, len_k} = 1] & \end{aligned} \right. \]

注意到下面的式子和 \(g\) 類似,可以在 \(O(nk)\) 之內找出最小字元 \(c\)

考慮上面的式子。

考慮直接把 \(j, k\) 兩維暴力壓成一維後用 \(\text{bitset}\) 維護。

二分當前的最小字元 \(c\),考慮預處理 \(h_{i, j}\) 表示第 \(j\) 個位置存在字典序小於等於 \(i\) 字元。

那麼一個很顯然的事情,當前最小字元為 \(c\) 合法,當且僅當 (h[c] & (f[i - 1] << 1))\(\text{bitset}\) 存在 \(1\)

考慮二分 \(c\),複雜度:\(O(\frac{n \times \sum |s_i|}{\omega} \times \log 26)\)

那麼轉移就很簡單了。

\[\left\{ \begin{aligned} f_{i, j, k} \to f_{i + 1, j, k + 1}, & & s_{j, k + 1} = c \\ f_{i, j, len_j} \to f_{i + 1, k, 1}, & & s_{k, 1} = c, g_{i + 1, k} = 1 \\ \end{aligned} \right. \]

實現細節較多。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <string>
#include <bitset>
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
    int p = 0, flg = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') flg = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        p = p * 10 + c - '0';
        c = getchar();
    }
    return p * flg;
}
string read_() {
    string ans;
    char c = getchar();
    while (c < 'a' || c > 'z') c = getchar();
    while (c >= 'a' && c <= 'z') ans += c, c = getchar();
    return ans;
}
void write(int x) {
    if (x < 0) {
        x = -x;
        putchar('-');
    }
    if (x > 9) {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}
bool _stmer;

const int N = 2e3 + 5, M = 1e6 + 5, K = 1e4 + 5;

array <int, N> len, fir, sum;

array <bitset <N>, K> g, suf;
array <bitset <M>, 27> h;
array <bitset <M>, 2> f;

bitset <M> pre, vis;

bool check(int v, int c) { return (h[c] & (f[v] << 1)).count(); }

bool _edmer;
int main() {
    cerr << (&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
/* #ifdef cxqghzj */
    /* freopen("_.txt", "r", stdin); */
/* #endif */
    int n = read(), m = read();
    string mp;
    /* write(f[0].count()), puts("#"); */
    for (int i = 1; i <= n; i++)
        vis[fir[i] = mp.size()] = 1, mp += read_(), len[i] = (sum[i] = mp.size()) - fir[i];
    for (int i = 1; i <= 26; i++)
        for (int j = 0; j < (int)mp.size(); j++)
            if (!vis[j])
                h[i][j + 1] = (mp[j] <= ('a' + i - 1));
    suf[m + 1][n + 1] = 1;
#define upd(x, y) (x = x | y)
    for (int j = n; j; j--) {
        for (int i = 1; i + len[j] - 1 <= m; i++)
            upd(g[i][j], suf[i + len[j]][j + 1]);
        for (int i = 1; i <= m + 1; i++) suf[i][j] = g[i][j] | suf[i][j + 1];
    }
    f[0][0] = 1, pre[1] = 1;
    int v = 0;
    string ans;
    for (int i = 0; i < m; i++) {
        int l = 1, r = 26;
        for (int j = 1; j <= n; j++) {
            if (pre[j] && g[i + 1][j]) r = min(r, mp[fir[j]] - 'a' + 1);
            pre[j + 1] = f[v][sum[j]] | pre[j];
        }
        pre = 0;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (check(v, mid)) r = mid;
            else l = mid + 1;
        }
        ans.push_back(r + 'a' - 1);
        f[v ^ 1] = h[r] & (f[v] << 1);
        bool flg = 0;
        for (int j = 0; j <= n; j++) {
            if (j && g[i + 1][j] && mp[fir[j]] <= r + 'a' - 1)
                upd(f[v ^ 1][fir[j] + 1], flg);
            flg |= f[v][sum[j]];
        }
        /* write(f[v ^ 1].count()), puts(""); */
        v ^= 1;
    }
    printf("%s\n", ans.c_str());
    return 0;
}

相關文章