G - Compress Strings
G - Compress Strings
Problem Statement
You are given strings .
Find the minimum length of a string that contains all these strings as substrings.
Here, a string contains a string as a substring if can be obtained by deleting zero or more characters from the beginning and zero or more characters from the end of .
Constraints
- is an integer.
- is a string consisting of lowercase English letters whose length is at least .
- The total length of is at most .
Input
The input is given from Standard Input in the following format:
Output
Print the answer as an integer.
Sample Input 1
3
snuke
kensho
uk
Sample Output 1
9
The string snukensho
of length contains all of , , and as substrings.
Specifically, the first to fifth characters of snukensho
correspond to , the fourth to ninth correspond to , and the third to fourth correspond to .
No shorter string contains all of , , and as substrings. Thus, the answer is .
Sample Input 2
3
abc
abc
arc
Sample Output 2
6
Sample Input 3
6
cmcmrcc
rmrrrmr
mrccm
mmcr
rmmrmrcc
ccmcrcmcm
Sample Output 3
27
解题思路
首先如果存在某个字符串 是另外一个字符串 的子串这种情况,可以直接忽略 ,因为构造出的字符串中包含 因此也必然包含 。为此我们先把所有满足该条件的 先删去,可以用 Z 函数 来实现。具体做法是对于每个 枚举剩下还没被删除的 ,拼接得到 并求 Z 函数,然后枚举 的部分(下标从 开始),如果发现 ,说明 与 匹配,即 是 的子串。
然后我们用剩余的字符串按任意顺序拼接出答案,这显然是可以的。考虑拼接得到的字符串中相邻的两个字符串 和 ,用 表示 的后缀与 的前缀的最大匹配长度,即使得 的最大的 。那么我们可以让答案的长度减少 。另外我们只需考虑相邻的字符串即可(不用再考虑更前面的字符串),因为不存在包含或被包含的关系。
因此就可以 dp 了,定义 表示在选择了 所表示的二进制集合中的字符串进行拼接,且最后一个字符串是 的所有方案中长度的最小值。根据前倒数第二个字符串是什么进行状态转移,转移方程就是 。
也可以用 Z 函数来求出来。枚举每一个 作为前一个字符串,再枚举剩余的 作为后一个字符串,拼接得到 并求 Z 函数。枚举 ,如果发现 ,说明 的后缀 与 的前缀 匹配,那么有 。
另外由于涉及到的都是字符串匹配,可以把 Z 函数换成简单粗暴的字符串哈希,但不建议这么做因为容易被卡。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
const int N = 25, M = 2e5 + 10;
string s[N];
int z[M];
bool vis[N];
int g[N][N];
int f[1 << 20][N];
void z_function(string &s) {
int n = s.size();
for (int i = 1, l = 0, r = 0; i < n; i++) {
z[i] = 0;
if (i <= r) z[i] = min(z[i - l], r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) {
z[i]++;
l = i, r = i + z[i] - 1;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> s[i];
}
vector<int> p;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j || vis[j]) continue;
string t = s[i] + s[j];
z_function(t);
for (int k = s[i].size(); k < t.size(); k++) {
if (z[k] >= s[i].size()) vis[i] = true;
}
}
if (!vis[i]) p.push_back(i);
}
n = p.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) continue;
string t = s[p[j]] + s[p[i]];
z_function(t);
for (int k = 0; k < s[p[i]].size(); k++) {
if (z[k + s[p[j]].size()] >= s[p[i]].size() - k) {
g[i][j] = s[p[i]].size() - k;
break;
}
}
}
}
memset(f, 0x3f, sizeof(f));
for (int i = 0; i < n; i++) {
f[0][i] = 0;
}
for (int i = 1; i < 1 << n; i++) {
for (int j = 0; j < n; j++) {
if (~i >> j & 1) continue;
for (int k = 0; k < n; k++) {
if (i >> k & 1) f[i][j] = min(f[i][j], f[i ^ 1 << j][k] + int(s[p[j]].size()) - g[k][j]);
}
}
}
int ret = 0x3f3f3f3f;
for (int i = 0; i < n; i++) {
ret = min(ret, f[(1 << n) - 1][i]);
}
cout << ret;
return 0;
}
参考资料
Editorial - AtCoder Beginner Contest 343:https://atcoder.jp/contests/abc343/editorial/9447
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18053429
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-03-05 异或值
2022-03-05 聪明的燕姿