隐藏页面特效

CF2045H - Missing Separators 题解

CF2045H - Missing Separators

题面

您有一本字典,它是按字母顺序排列的多个单词的列表。每个单词都由大写英文字母组成。

您想打印这本字典。然而,打印系统出现了一个错误,列表中的所有单词都紧挨着打印,单词之间没有任何分隔符。现在,您最终得到的字符串 S 是字典中所有单词按照所列顺序连接而成的。

您的任务是将 S 拆分为一个或多个单词,从而重建字典。请注意,重建后的字典必须由按字母顺序排列的不同单词组成。此外,您还要最大限度地增加字典中的单词数量。如果有多部字词数最多的词典,您可以任选一部。


题解

这场比赛全是神仙题,orz。

考虑设计 dp 数组,dp[x][y] 表示将 S[xy] 分割成为一个单词的最多单词数。

可以发现 dp[x][y]dp[y+1][z] 的转移当且仅当 S[xy] 的字典序比 S[y+1z] 的字典序小。

唯一的问题是,这个 dp 方式是 O(n3) 的,我们不能接受,因此我们考虑找到一个关键点 z,使得 S[xy] 的字典序比 S[y+1z] 的字典序小,那么 dp[y+1][z+k](k0) 的情况可以由前者覆盖。

我们考虑这个点 z 如何寻找,可以发现我们可以求出 S[xy]S[y+1]最长公共前缀,他们具有最长公共前缀的长度为 k,显而易见这个 k 即是我们所求的 z

我们考虑如何求出 S[xy]S[y+1] 的最长公共前缀 LCPS[i][j],我们可以倒序枚举 S[i]S[j],如果 S[i]=S[j],那么 LCPS[i][j]=LCPS[i+1][j+1]+1,这样我们可以预处理出任意后缀的最长公共前缀了。

接下来考虑转移,对于一个位,首先可以继承前者状态 dp[i][j]=dp[i][j1],往分割后的字符串后加上一个字符不改变数量。另一方面,对于具有最长公共前缀长度为 k 的两个相邻串 dp[x][y] 可以转移到 dp[y+1][y+1+k],因为 S[xy]S[y+1y+1+k] 短,所以字典序一定更小。在转移中记录一下前一字符串的开头即可,方便后续输出答案。

时间复杂度 O(|S|2)

参考代码

复制
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5010; char s[N]; int dp[N][N], LCPS[N][N]; int pre[N][N]; int main() { scanf("%s", s + 1); int n = strlen(s + 1); for (int i = n; i; i -- ) { for (int j = n; j; j -- ) { if (s[i] == s[j]) LCPS[i][j] = LCPS[i + 1][j + 1] + 1; dp[i][j] = -1e9; } } dp[1][0] = 1; for (int i = 1; i <= n; i ++ ) { for (int j = i; j <= n; j ++ ) { if (dp[i][j - 1] > dp[i][j]) { dp[i][j] = dp[i][j - 1]; pre[i][j] = pre[i][j - 1]; } if (j == n) continue; int k = min(j - i + 1, LCPS[i][j + 1]); if (j + 1 + k > n) continue; if (i + k > j || s[i + k] < s[j + 1 + k]) { if (dp[i][j] + 1 > dp[j + 1][j + 1 + k]) { dp[j + 1][j + 1 + k] = dp[i][j] + 1; pre[j + 1][j + 1 + k] = i; } } } } int cnt = 0, last = 0, ed = n; for (int i = 1; i <= n; i ++ ) if (dp[i][n] > cnt) cnt = dp[i][n], last = i; vector<int> ans(1, n + 1); while (last) { int t = pre[last][ed]; ans.push_back(last); ed = last - 1, last = t; } reverse(ans.begin(), ans.end()); cout << cnt << "\n"; for (int i = 0; i + 1 < ans.size(); i ++ , cout << "\n") for (int j = ans[i]; j < ans[i + 1]; j ++ ) cout << s[j]; return 0; }

__EOF__

本文作者Yip.Chip
本文链接https://www.cnblogs.com/YipChipqwq/p/-/CF2045H.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   YipChip  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示