[Jsoi2014]回文串 Hash+树状数组

本文介绍了一种针对给定字符串区间内回文串数量查询的高效算法。通过利用回文串中心对称特性,结合树状数组进行区间贡献计算,实现快速查询。算法详细解析了如何将问题分解为左右半区间的贡献,并提供了完整代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给定一个字符串,每次询问lll~rrr间有多少个回文串。


Sample Input
abaa
4
1 2
1 3
1 4
3 4


Sample Output
2
4
6
3


考虑每个回文中心做出的贡献,假设它拓展的最长长度为sss,它的位置为p,当前询问为l,rl,rl,r
那么他的贡献就是:min(len,r−p+1,p−l+1)min(len,r-p+1,p-l+1)min(len,rp+1,pl+1)
我们考虑把它分成两半,[l,mid][l,mid][l,mid],[mid+1,r][mid+1,r][mid+1,r]
那么其实只有在中间的部分才会同时受到lllrrr的限制,特殊处理即可。
对于左边的,式子变成:min(len,p−l+1)min(len,p-l+1)min(len,pl+1)。即p−len+1&gt;=lp-len+1&gt;=lplen+1>=l算为lenlenlen的贡献,p−len+1&lt;lp-len+1&lt;lplen+1<l算为p−l+1p-l+1pl+1的贡献。
可以考虑用树状数组处理。


#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const ULL P = 131;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

struct hh {
	int l, s;
} a[100010];
struct node {
	int l, r, id;
} a1[300010], a2[300010]; int tp;
ULL s1[100010], s2[100010], o[100010];
int n, mc1[100010], mc2[100010];
LL s[100010], ans[300010];
int num[100010];
char ss[100010];

int lowbit(int x) {return x & -x;}
void change(int x, int c) {for(int i = x; i <= n; i += lowbit(i)) s[i] += c;}
void cnum(int x, int c) {for(int i = x; i <= n; i += lowbit(i)) num[i] += c;}
LL getsum(int x) {LL sum = 0; for(int i = x; i; i -= lowbit(i)) sum += s[i]; return sum;}
int getn(int x) {int sum = 0; for(int i = x; i; i -= lowbit(i)) sum += num[i]; return sum;}

bool cmp1(node a, node b) {return a.l < b.l;}
bool cmp2(node a, node b) {return a.r > b.r;}
bool cmp3(hh a, hh b) {return a.l - a.s < b.l - b.s;}
bool cmp4(hh a, hh b) {return a.l + a.s > b.l + b.s;}

int main() {
	scanf("%s", ss + 1);
	n = strlen(ss + 1);
	s1[0] = 0; for(int i = 1; i <= n; i++) s1[i] = s1[i - 1] * P + ss[i];
	s1[n + 1] = 0; for(int i = n; i >= 1; i--) s2[i] = s2[i + 1] * P + ss[i];
	o[0] = 1; for(int i = 1; i <= n; i++) o[i] = o[i - 1] * P;
	for(int i = 1; i <= n; i++) {
		int l = 1, r = _min(i, n - i + 1), ans = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s1[i + mid - 1] - s1[i - 1] * o[mid] == s2[i - mid + 1] - s2[i + 1] * o[mid]) l = mid + 1, ans = mid;
			else r = mid - 1;
		} mc1[i] = ans;
		if(i == n) continue;
		l = 1, r = _min(i, n - i + 1), ans = 0;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(s1[i + mid] - s1[i] * o[mid] == s2[i - mid + 1] - s2[i + 1] * o[mid]) l = mid + 1, ans = mid;
			else r = mid - 1;
		} mc2[i] = ans;
	}
	int m = read();
	for(int i = 1; i <= m; i++) {
		int l = read(), r = read();
		if(l == r) {ans[i] = 1; continue;}
		if((l + r) & 1) {
			int mid = (l + r) / 2;
			ans[i] += _min(mc2[mid], mid - l + 1);
			a1[++tp].l = l, a1[tp].r = mid, a1[tp].id = i;
			a2[tp].l = mid + 1, a2[tp].r = r, a2[tp].id = i;
		} else {
			int mid = (l + r) / 2;
			ans[i] += _min(mc1[mid], r - mid + 1);
			ans[i] += _min(mc2[mid - 1], mid - l) + _min(mc2[mid], r - mid);
			a1[++tp].l = l, a1[tp].r = mid - 1, a1[tp].id = i;
			a2[tp].l = mid + 1, a2[tp].r = r, a2[tp].id = i;
		}
	} sort(a1 + 1, a1 + tp + 1, cmp1), sort(a2 + 1, a2 + tp + 1, cmp2);
	for(int i = 1; i <= n; i++) a[i].l = i, a[i].s = mc1[i];
	sort(a + 1, a + n + 1, cmp3); int gg = 0;
	memset(s, 0, sizeof(s)); memset(num, 0, sizeof(num));
	for(int i = 1; i <= n; i++) change(i, mc1[i]);
	for(int i = 1; i <= tp; i++) {
		while(gg < n && a1[i].l > a[gg + 1].l - a[gg + 1].s + 1) ++gg, change(a[gg].l, a[gg].l - a[gg].s), cnum(a[gg].l, 1);
		ans[a1[i].id] += getsum(a1[i].r) - getsum(a1[i].l - 1) - (LL)(getn(a1[i].r) - getn(a1[i].l - 1)) * (a1[i].l - 1);
	}
	for(int i = 1; i < n; i++) a[i].l = i, a[i].s = mc2[i];
	sort(a + 1, a + n, cmp3); gg = 0;
	memset(s, 0, sizeof(s)); memset(num, 0, sizeof(num));
	for(int i = 1; i < n; i++) change(i, mc2[i]);
	for(int i = 1; i <= tp; i++) {
		if(a1[i].l == a1[i].r) continue;
		while(gg < n - 1 && a1[i].l > a[gg + 1].l - a[gg + 1].s + 1) ++gg, change(a[gg].l, a[gg].l - a[gg].s), cnum(a[gg].l, 1);
		ans[a1[i].id] += getsum(a1[i].r - 1) - getsum(a1[i].l - 1) - (LL)(getn(a1[i].r - 1) - getn(a1[i].l - 1)) * (a1[i].l - 1);
	}
	for(int i = 1; i <= n; i++) a[i].l = i, a[i].s = mc1[i];
	sort(a + 1, a + n + 1, cmp4); gg = 0;
	memset(s, 0, sizeof(s)), memset(num, 0, sizeof(num));
	for(int i = 1; i <= n; i++) change(i, mc1[i]);
	for(int i = 1; i <= tp; i++) {
		while(gg < n && a2[i].r < a[gg + 1].l + a[gg + 1].s - 1) ++gg, change(a[gg].l, -a[gg].l - a[gg].s), cnum(a[gg].l, 1);
		ans[a2[i].id] += getsum(a2[i].r) - getsum(a2[i].l - 1) + (LL)(getn(a2[i].r) - getn(a2[i].l - 1)) * (a2[i].r + 1);
	}
	for(int i = 1; i < n; i++) a[i].l = i, a[i].s = mc2[i];
	sort(a + 1, a + n, cmp4); gg = 0;
	memset(s, 0, sizeof(s)), memset(num, 0, sizeof(num));
	for(int i = 1; i <= n; i++) change(i, mc2[i]);
	for(int i = 1; i <= tp; i++) {
		if(a2[i].l == a2[i].r) continue;
		while(gg < n && a2[i].r < a[gg + 1].l + a[gg + 1].s) ++gg, change(a[gg].l, -a[gg].l - a[gg].s), cnum(a[gg].l, 1);
		ans[a2[i].id] += getsum(a2[i].r - 1) - getsum(a2[i].l - 1) + (LL)(getn(a2[i].r - 1) - getn(a2[i].l - 1)) * a2[i].r;
	} for(int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值