「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)

ECJTU 2018 Summer Training 2

Summer training round2 #8(Training26)

Summer training round2 #6 (Training #26)

ecjtu-summer training #11