「ZJU Summer Training 2020 - Round 2/3」部分补题记录
Posted -wallace-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「ZJU Summer Training 2020 - Round 2/3」部分补题记录相关的知识,希望对你有一定的参考价值。
Content
- ZJU-ICPC Summer 2020 Contest 4 by Group A
-
- Problem F. Magical String
ZJU-ICPC Summer 2020 Contest 4 by Group A
Problem F. Magical String
给定一个一个文本串 (A(|A| le 500)),以及一个大小为 (m) 的模式串集 (B = {B_1, B_2, cdots, B_m}(mle 10^5, |B_i| le |A|, sum|B_i| le 10^6))。试选出一个 (B) 的子集 (S),满足 (S) 中的字符串存在一种排列方式(可以任意排列),使排列后的新字符串 (C) 无限向两端复制的结果等价于 (A) 向两端无限复制的结果。求 (min |S|)。
非常显然,若 (S) 可以匹配到 (A) 的一个循环节,那么整个都可以匹配。而 (A) 的一个循环节无限复制的结果和 (A) 本身复制的结果是相同的。于是我们之间匹配 (A) 的 最小循环节 即可。记这个最小循环节长度为 (L)。
我们处理出 (B) 中的模式串在 (A) 中的出现情况。对于 (A) 的某一个位置 (i),若 (A[i, j]) 是 (B) 中的一个串,那么我们称 第 (i) 个位置可以转移到第 (j+ 1) 个位置。但可能会超出最小循环节,于是令 (j) 对 (L) 取模。
考虑如何对于所有的 (i in [1, L]),处理出所有从 (i) 开始的转移。由于转移中的 (B) 一定是 (A[i, |A|]) 的 一个前缀,当一个位置断掉了,之后一定不会匹配。可以用 Trie 树辅助实现这一过程。
现在我们已经得到了所有转移,那么我们不妨 将位置视为点,将转移视为边,构成了一个有向图。 其中每使用一次转移,就相当于使用 (B) 中 的一个元素。这个元素并不会重用,因为处理出了 (A) 的最小循环节。
那么我们要求的答案就是 最小环的大小。这个直接 Floyd 可以过,复杂度 (O(n^3))。
思路主要来自 kqh (orz)
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Article : ZJU Summer Training 2020
*/
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1005;
const int L = 1e6 + 5;
const int M = 1e5 + 5;
const int S = 26;
int n, m;
char s[N];
char t[N];
int g[N][N];
int f[N][N];
struct trie {
int ch[L][S];
int total;
bool end[L];
trie() {
total = 1;
}
void insert(char* s) {
int x = 1;
for (int i = 0; s[i]; i++) {
int c = s[i] - ‘a‘;
if (!ch[x][c]) ch[x][c] = ++total;
x = ch[x][c];
}
end[x] = 1;
}
inline int* operator [] (int p) {
return ch[p];
}
} tr;
bool same(char* a, char* b, int l){
for (; l; --l, ++a, ++b) if (*a != *b) return false;
return true;
}
signed main() {
scanf("%s", s + 1);
n = strlen(s + 1);
int cl = n;
for (int l = n; l; --l) {
if (n % l != 0) continue;
bool f = 1;
for (register int start = l + 1; start <= n && f; start += l)
if (!same(s + 1, s + start, l)) f = false;
if (f) cl = l;
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%s", t);
tr.insert(t);
}
copy(s + 1, s + 1 + n, s + 1 + n);
memset(g, 0x3f, sizeof g);
for (int i = 1; i <= cl; i++) {
int x = 1;
for (int j = i; j <= n + i; j++) {
x = tr[x][s[j] - ‘a‘];
if (!x) break;
if (tr.end[x]) {
int k = j % cl + 1;
if (k == i) { puts("1"); return 0;}
g[i][k] = 1;
}
}
}
for (register int i = 1; i <= cl; i++)
g[i][i] = 0;
memcpy(f, g, sizeof g);
long long ans = 0x3f3f3f3f;
for(int k = 1; k <= cl; k++)
for(int i = 1; i <= cl; i++)
for(int j = 1; j <= cl; j++)
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
for(int i = 1; i <= cl; i++)
for(int j = 1; j <= cl; j++)
if (i != j)
ans = min(ans, 1ll * f[i][j] + f[j][i]);
printf("%lld
", ans > n * 2 ? -1 : ans);
return 0;
}
以上是关于「ZJU Summer Training 2020 - Round 2/3」部分补题记录的主要内容,如果未能解决你的问题,请参考以下文章
ZJU-ICPC Summer 2020 Contest 7 D Cafeterias
Summer training round2 #9(Training28)
Summer training round2 #8(Training26)