[ARC058F] Iroha Loves Strings

cxqghzj·2024-03-29 21:54·9 次阅读

[ARC058F] Iroha Loves Strings

题意#

给定 n 个字符串 s1,s2,...,sn

你需要在其中选择一些字符串,按照顺序拼接。

在所有生成的长度为 k 的字符串中,选择字典序最小的一个。

n2000,k104,|si|106

Sol#

考虑一个朴素的 dp。

fi,j 表示前 i 个字符串,放到了前 j 个位置的最小的字符串。

这个 dp 的复杂度为 nk×|si|

不难注意到一个事情,如果当前放的字符串 si 已经不是最优的字典序了,那么无论后面怎么放,此处放 si 都不可能成为最优方案。

考虑想的暴力一点:

fi,j,k 表示用第 j 个字符串的第 k 位填在第 i 位,是否可以为当前字典序最小的方案,且合法。

先考虑简单的合法,每次新放一个字符串都需要判断放当前字符串是否可以组成长度为 k 的答案。

gi,j 表示用第 j 个字符串填完前 i 位是否能合法。

显然:

k[j+1,n],gi,j=gi,jgi+lenj,k

gi,j 显然可以在 O(nk) 的时间求出。

考虑当前的最小字符。

注意到当前可以填的最小字符 sj,k 满足:

{minj,ksj,k+1,[fi1,j,k=1]minjsj,1,[k,k<j,fi1,k,lenk=1]

注意到下面的式子和 g 类似,可以在 O(nk) 之内找出最小字符 c

考虑上面的式子。

考虑直接把 j,k 两维暴力压成一维后用 bitset 维护。

二分当前的最小字符 c,考虑预处理 hi,j 表示第 j 个位置存在字典序小于等于 i 字符。

那么一个很显然的事情,当前最小字符为 c 合法,当且仅当 (h[c] & (f[i - 1] << 1))bitset 存在 1

考虑二分 c,复杂度:O(n×|si|ω×log26)

那么转移就很简单了。

{fi,j,kfi+1,j,k+1,sj,k+1=cfi,j,lenjfi+1,k,1,sk,1=c,gi+1,k=1

实现细节较多。

Code#

Copy
#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; }
posted @   cxqghzj  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录