2017 CCPC哈尔滨站 Palindrome (马拉车+树状数组)

题目定义了 “一个半回文串”,其实就是形如abab,是一个长度3n-2的字符串,其中子串[1,2n-1]是长度为奇数的回文串,[n,3n-2]同样是长度为奇数的回文串。

那么所求可转化如下。

首先,因为回文串长度均为奇数,因此我们不需要考虑偶数长度的回文子串。

假设位置i的字符作为回文子串的中心时的最大回文半径为r[i](这个显然可以通过马拉车算法预处理出),则我们要求的是这样的 (i,j)的对数:

假设i>j,则有 i-r[i]>=j && j+r[j]>=i,因为只有这样的一对位置,才可以保证能形成符合要求的“一个半回文串”。

这个感觉很像是一个二维偏序问题。

对所有的(i-r[i],i)按i-r[i]从小到大排序,从小到大枚举j,因为只有i-r[i]小于等于当前j的i才可能与之形成符合要求的 (i,j),因此将所有i-r[i]小于等于当前j的i插入树状数组(即将对应位置+1),然后,统计在(j,j+r[j]]之间的和即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define mst(a,b) memset(a,b,sizeof(a))
#define ll long long
const int MAXN=5e5+5;
char Ma[MAXN];
int Mp[MAXN];
void Manacher(char s[],int len){
//因为只需要求奇数长度的回文串,所以除了起始字符外不用添加特殊字符 
	int l=len+1;
	Ma[0]='$';
	Ma[l]=0;
	int mx=0,id=0;
	for(int i=0;i<l;i++){
		Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
		while(Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
		if(i+Mp[i]>mx){
			mx=i+Mp[i];
			id=i;
		}
	}
}
int t;
int c[MAXN];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int z=1){
	while(x<MAXN){
		c[x]+=z;
		x+=lowbit(x);
	}
}
int query(int x){
	int res=0;
	while(x>0){
		res+=c[x];
		x-=lowbit(x);
	}
	return res;
}
struct node{
	int d,pos; 
	bool operator<(const node &p)const{//按差值排序 
		return d<p.d;
	}
}a[MAXN];
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%s",Ma+1);
		int len=strlen(Ma+1);
		Manacher(Ma,len);
		mst(c,0);
		for(int i=1;i<=len;i++){ 
			Mp[i]--;
		}
		ll  ans=0;
		int cnt=0;
		for(int i=len;i>=1;i--){ 
			a[cnt].d=i-Mp[i];
			a[cnt].pos=i;
			cnt++;
		}
		sort(a,a+cnt);
		for(int i=1,j=0;i<len;i++){
			while(j<cnt&&a[j].d<=i){
				add(a[j].pos);
				j++;
			}
			ans+=query(i+Mp[i])-query(i);
		}
		cout<<ans<<"\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38515845/article/details/89332694