码迷,mamicode.com
首页 > 其他好文 > 详细

「ZJU Summer Training 2020 - Round 2/3」部分补题记录

时间:2020-07-17 19:24:42      阅读:54      评论:0      收藏:0      [点我收藏+]

标签:oid   情况   子集   使用   long   过程   emc   bool   方式   

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\}(m\le 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\n", ans > n * 2 ? -1 : ans);
	return 0;
}

「ZJU Summer Training 2020 - Round 2/3」部分补题记录

标签:oid   情况   子集   使用   long   过程   emc   bool   方式   

原文地址:https://www.cnblogs.com/-Wallace-/p/13329319.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!