Coodeforces 744E - Hongcow Masters the Cyclic Shift(自动机+缩点)

Codeforces 题目传送门 & 洛谷题目传送门

首先很显然发现集合的合法性满足包含单调,因此套个 two pointers 问题就转化为 \(\mathcal O(n)\) 次 check 一个集合的合法性。

我们重新审视一下比较两个字符串 \(S=x_{k_1}+x_{k_2}+x_{k_3}+\cdots+x_{k_p}\)\(T=x_{l_1}+x_{l_2}+x_{l_3}+\cdots+x_{l_q}\) 的过程。我们考虑维护两个字符串 \(S_0,T_0\),每次支持往 \(S_0,T_0\) 之一加入下一个元素,如果匹配到了不同的字符就推出。显然我们必然存在一种加字符串的顺序使得每次恰好往 \(S_0,T_0\) 中长度较短的一者加,那么这样以来就有以下三类可能出现的状态。

  • \(X(s)\):目前 \(S_0\)\(T_0\)\(s\) 这个字符串。
  • \(Y(s)\):目前 \(T_0\)\(S_0\)\(s\) 这个字符串。
  • \(Z\):目前 \(S_0=T_0\)

考虑对这个东西建自动机。由于二者之差肯定是字符串的某个后缀,所以总状态数是 \(\mathcal O(\sum|s|)\) 的。而每个状态最多只会转移到 \(n\) 个状态,这部分可以哈希处理,所以这是一张 \(\mathcal O(\sum|s|)\) 个点 \(\mathcal O(\sum|s|·n)\) 条边的图。

那么我们又该如何判定合法性呢?不难发现一个集合不合法,等价于存在某个字符串 \(s\) 的某个前缀 \(1\le l<|s|\) 使得 \(X(s[l+1...|s|])\) 可以到达 \(Y(s[1...|s|])\)。证明显然。有向图任意两点可达性?一氧化氮。注意到 \(Y(s[1...|s|])\) 必然可以到达 \(X(s[l+1...|s|])\),因此只需判它们是否在同一 SCC 中,Tarjan 缩点即可。

时间复杂度 \(n^2·\sum|s|\)

const int MAXN = 30;
const int MAXM = 1e5;
const int MAXV = MAXM * 2;
const int MAXE = 1e7;
const int BS1 = 191;
const int BS2 = 193;
const int MOD1 = 993244853;
const int MOD2 = 1004535809;
int n, res, len[MAXN + 5]; char s[MAXN + 5][MAXM + 5];
int hs1[MAXN + 5][MAXM + 5], pw1[MAXM + 5];
int hs2[MAXN + 5][MAXM + 5], pw2[MAXM + 5];
u64 gethash(int x, int l, int r) {
	int HS1 = (hs1[x][r] - 1ll * pw1[r - l + 1] * hs1[x][l - 1] % MOD1 + MOD1) % MOD1;
	int HS2 = (hs2[x][r] - 1ll * pw2[r - l + 1] * hs2[x][l - 1] % MOD2 + MOD2) % MOD2;
	return (1ull * HS1) << 32 | HS2;
}
int hd[MAXV + 5], to[MAXE + 5], nxt[MAXE + 5], ec = 0;
void adde(int u, int v) {to[++ec] = v; nxt[ec] = hd[u]; hd[u] = ec;}
unordered_map<u64, int> id[2]; int ncnt, EQUAL;
namespace Tarjan {
	int dfn[MAXV + 5], low[MAXV + 5], tim, stk[MAXV + 5], tp, bel[MAXV + 5], vis[MAXV + 5], cmp;
	void init() {
		memset(dfn, 0, sizeof(dfn));
		memset(low, 0, sizeof(low));
		tim = cmp = tp = 0;
	}
	void tarjan(int x) {
		dfn[x] = low[x] = ++tim; stk[++tp] = x; vis[x] = 1;
		for (int e = hd[x]; e; e = nxt[e]) {
			int y = to[e];
			if (!dfn[y]) tarjan(y), chkmin(low[x], low[y]);
			else if (vis[y]) chkmin(low[x], dfn[y]);
		}
		if (dfn[x] == low[x]) {
			++cmp; int o;
			do {
				o = stk[tp--];
				vis[o] = 0; bel[o] = cmp;
			} while (o ^ x);
		} 
	}
	void work() {for (int i = 1; i <= ncnt; i++) if (!dfn[i]) tarjan(i);}
}
void init() {
	Tarjan :: init();
	memset(hd, 0, sizeof(hd)); ec = 0;
	for (int i = 0; i < 2; i++) id[i].clear();
	ncnt = EQUAL = 0;
}
bool check(int l, int r) {
	init();
	for (int i = l; i <= r; i++) {
		for (int j = 1; j <= len[i]; j++) {
			u64 hs = gethash(i, j, len[i]);
			if (!id[0][hs]) id[0][hs] = ++ncnt;
			if (!id[1][hs]) id[1][hs] = ++ncnt;
		}
	}
	EQUAL = ++ncnt;
	for (int i = l; i <= r; i++) for (int j = 1; j <= len[i]; j++)
		for (int k = l; k <= r; k++) {
			for (int x = 0; x < 2; x++) {
				int A = len[i] - j + 1, B = len[k];
				if (A > B) {
					if (gethash(i, j, j + B - 1) != gethash(k, 1, B)) continue;
					adde(id[x][gethash(i, j, len[i])], id[x][gethash(i, j + B, len[i])]);
				} else if (A == B) {
					if (gethash(i, j, len[i]) != gethash(k, 1, B)) continue;
					adde(id[x][gethash(i, j, len[i])], EQUAL);
				} else {
					if (gethash(i, j, len[i]) != gethash(k, 1, A)) continue;
					adde(id[x][gethash(i, j, len[i])], id[x ^ 1][gethash(k, A + 1, B)]);
				}
			}
		}
	for (int i = l; i <= r; i++) {
		adde(EQUAL, id[0][gethash(i, 1, len[i])]);
		adde(EQUAL, id[1][gethash(i, 1, len[i])]);
	}
	Tarjan :: work();
	for (int i = l; i <= r; i++) {
		for (int j = 1; j < len[i]; j++) {
			if (!id[0].count(gethash(i, 1, j))) continue;
			int X = id[0][gethash(i, 1, j)], Y = id[1][gethash(i, j + 1, len[i])];
			if (Tarjan :: bel[X] == Tarjan :: bel[Y]) return 0;
		}
	}
	return 1;
}
int main() {
	scanf("%d", &n);
	for (int i = (pw1[0] = pw2[0] = 1); i <= MAXM; i++) {
		pw1[i] = 1ll * pw1[i - 1] * BS1 % MOD1;
		pw2[i] = 1ll * pw2[i - 1] * BS2 % MOD2;
	}
	for (int i = 1; i <= n; i++) {
		scanf("%s", s[i] + 1); len[i] = strlen(s[i] + 1);
		for (int j = 1; j <= len[i]; j++) hs1[i][j] = (1ll * hs1[i][j - 1] * BS1 + s[i][j]) % MOD1;
		for (int j = 1; j <= len[i]; j++) hs2[i][j] = (1ll * hs2[i][j - 1] * BS2 + s[i][j]) % MOD2;
	}
	for (int l = 1, r = 1; l <= n; l++) {
		while (r <= n && check(l, r)) ++r;
		res += r - l;
	}
	printf("%d\n", res);
	return 0;
}
posted @ 2022-07-10 17:00  tzc_wk  阅读(43)  评论(0编辑  收藏  举报