CF961F k-substrings(哈希 + 二分 + 线段树)

CF961F k-substrings

哈希 + 二分 + 线段树

首先需要转变一下角度,容易发现如果按每个 k 计算答案,会计算多次相同答案的贡献。于是从答案对子串的贡献入手,枚举答案。因为 k 子串中心对称,于是 t 的中心也中心对称,枚举前缀的中心为 i,那么对应后缀的中心为 ni+1,现在需要找到前缀等于后缀的最长半径。

显然可以二分,并且判断字符串相等可以用字符串哈希解决。那么假如半径为 r,那么从产生贡献的最左端 ir+1 出发,贡献依次是 2r1,2r31,最右端为 i。 现在需要区间维护等差数列,单点查询。

找个数据结构维护。观察到所有等差数列的差值都为 2,于是有个 trick:对于每个 j[ir+1,i],贡献写成 2r1+2(ir+1)2j,这样每个等差数列的贡献就是前面的部分,后面的 2j 共有,只需要区间修改即可,都可以用线段树维护。

复杂度 O(nlogn)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10, p = 20242024, mod = 1234567891;

int n;
std::string s;
i64 hsh[N], pw[N];

bool check(int x, int i, int j) {
	int l1 = i - x + 1, r1 = i + x - 1, l2 = j - x + 1, r2 = j + x - 1;
	if(l1 < 1 || r2 > n || l2 < 1 || l2 > n) return 0;

	i64 ret1 = (hsh[r1] - hsh[l1 - 1] * pw[2 * x - 1] % mod + mod) % mod, ret2 = (hsh[r2] - hsh[l2 - 1] * pw[2 * x - 1] % mod + mod) % mod;

	if(ret1 == ret2) return 1;
	return 0;
}

struct SEG {
	int max, lzy;
} t[N << 2];
void pushup(int u) {
	t[u].max = std::max(t[u << 1].max, t[u << 1 | 1].max);
}
void mdf(int u, int x) {
	t[u].max = std::max(t[u].max, x);
	t[u].lzy = std::max(t[u].lzy, x);
}
void pd(int u) {
	if(!t[u].lzy) return;
	mdf(u << 1, t[u].lzy), mdf(u << 1 | 1, t[u].lzy);
	t[u].lzy = 0;
}
void upd(int u, int l, int r, int L, int R, int x) {
	if(L <= l && r <= R) {
		mdf(u, x);
		return;
	}
	int mid = (l + r) >> 1; pd(u);
	if(L <= mid) upd(u << 1, l, mid, L, R, x);
	if(R > mid) upd(u << 1 | 1, mid + 1, r, L, R, x);
	pushup(u);
} 
int qry(int u, int l, int r, int x) {
	if(l == r) return t[u].max;
	int mid = (l + r) >> 1; pd(u);
	if(x <= mid) return qry(u << 1, l, mid, x);
	return qry(u << 1 | 1, mid + 1, r, x);
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> s;
	s = '#' + s;

	for(int i = 1; i <= n; i++) {
		hsh[i] = (hsh[i - 1] * p + s[i]) % mod;
	}
	pw[0] = 1;
	for(int i = 1; i <= n; i++) pw[i] = pw[i - 1] * p % mod;

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