P7114 [NOIP2020] 字符串匹配 (哈希)

P7114 [NOIP2020] 字符串匹配

看到循环部分 AB,自然想要去枚举它,并且用哈希。开始想到的是倍增+hash求出最长循环的右端点,复杂度是 O(nlogn),结果不好写,没写出来。

我们先思考找到右端点怎么计算贡献。最朴素的,我们再枚举前缀 ABABAB,容易预处理出后缀出现奇数次的字符数量,所以当前贡献即为 AB 能有多少种分割使得 A 中出现奇数次的字符数量小于后缀的。看出可以把值域看作一维,树状数组单点插入,前缀和查询即可。

看到题解关于复杂度的一个结论 i=1nninlnnnlogn

我们直接枚举比倍增还快,这样代码变简单,复杂度降为 O(n(lnn+log26)),还是不保险。

我们考虑如何简化贡献的计算,发现对于后缀 ABABC,它出现奇数次的字符数量和 C 是一样的;而 ABABABCABC 是一样的。所以我们将后缀分为奇偶两部分,每部分的贡献只需要在树状数组求出的值上乘一个系数(即能出现多少次奇偶相同的)。

复杂度为 O(nlnn+log26)

#include <bits/stdc++.h>
typedef long long ll;
int read() {
	int x = 0, f = 1;
	char c = getchar();
	while(!isdigit(c)) {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(isdigit(c)) {
		x = (x << 3) + (x << 1) + (c - '0');
		c = getchar();
	} 
	return x * f;
}
const ll p = 1331, mod = 13333331;
ll t, n, h[2000010], pw[2000010], suf[2000010], vis[30], ans, c[30];
char s[2000010];
ll hsh(int l, int r) {
	return (h[r] - h[l - 1] * pw[r - l + 1] % mod + mod) % mod;
}
ll lowbit(ll x) {
	return x & (-x);
}
void update(int x, ll y) {
	for(int i = x; i <= 27; i += lowbit(i)) {
		c[i] += y;
	}
}
ll query(int x) {
	ll ret = 0;
	for(int i = x; i; i -= lowbit(i)) {
		ret += c[i];
	}
	return ret;
}
void Solve() {
	t = read();
	pw[0] = 1;
	for(int i = 1; i <= 2000000; i++) {
		pw[i] = pw[i - 1] * p % mod; 
	}
	while(t--) {
		ans = 0;
		std::cin >> s + 1;
		n = strlen(s + 1);
		for(int i = 1; i <= n; i++) {
			h[i] = (h[i - 1] * p + s[i]) % mod;
		}
		int now = 0;
		memset(vis, 0, sizeof(vis));
		memset(c, 0, sizeof(c));
		for(int i = n; i >= 1; i--) {
			vis[s[i] - 'a']++;
			if(vis[s[i] - 'a'] % 2 == 1) now++;
			else now--;
			suf[i] = now;
		}
		memset(vis, 0, sizeof(vis));
		now = 0;
		for(int i = 1; i < n; i++) {
			int r = i;
			for(int j = i; j <= n; j += i) {
				// std::cout << i << " " << j << " " << h[i] << " " << hsh(j - i + 1, j) << "\n";
				if(h[i] != hsh(j - i + 1, j) || j == n) {
					break;
				}
				else r = j;
			}
//			std::cout << i << " " << r << " ";
			int res1 = query(suf[r + 1] + 1), res2 = query(suf[r - i + 1] + 1);
//			std::cout << res1 << "\n";
			if(i != 1) {
				if(r / i == 1) {
					ans += res1;
				}
				else {
					if(((r / i) % 2) == 0) {
						ans += (r / i / 2) * res1;
					}
					else ans += (r / i / 2 + 1) * res1;
					if(((r / i + 1) % 2) == 0) {
						ans += ((r / i + 1) / 2 - 1) * res2; 
					}
					else ans += ((r / i + 1) / 2) * res2;
				}
			}
			// std::cout << r << "\n";
			vis[s[i] - 'a']++;
			if(vis[s[i] - 'a'] % 2 == 1) now++;
			else now--;
			update(now + 1, 1);
		}
		std::cout << ans << "\n";
	}
}

int main() {
	
	Solve();

	return 0;
}
posted @   Fire_Raku  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示