[HDU6230] Palindrome(CCPC2017哈尔滨站A题)Manacher+树状数组

题意是给出一个字符串,找出满足条件的子串。

子串S的要求:

1.长度为S[1..3n2(n2)

2. S[i]=S[2ni]=S[2n+i2](1in)


解题思路:从要求可以看出要求的子串是两个回文子串的叠加。
                 

即子串可以看成 S[1...i...j...3n-2] (其中i=n; j=2n-i) ,S[1...j] 和S[i...3n-2] 分别为回文串,并且长度均为奇数。

用Manacher可以求出以i和j为中点的最大回文串半径 Mp[i]和Mp[j](不包含本身)

然后问题就可以转化为求i,j对满足条件:

1. i<j

2.i+Mp[i]>=j

3.j-Mp[j]<=j

即对每个i,求区间[i+1,i+Mp[i]]中j的个数,满足j-Mp[j]<=i

双条件区间求个数,可以用主席树

本题由于i是从小到大循环,可以对树状数组进行预处理,每次把j-Mp[j]==i的位置更新,再求区间个数。

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define N 500010

char Ma[N*2];
int Mp[N*2],s[N],n;

struct DD{
	int num,v;
};

void Manacher(char *s,int len){
	int l=0;
	Ma[l++]='$';
	Ma[l++]='#';
	for (int i=0; i<len; i++){
		Ma[l++]=s[i];
		Ma[l++]='#';
	}
	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;
		}
	}
}

inline int lowbit(int x){ return x&(-x); }

LL getsum(int x){
	LL ret=0;
	while (x  > 0){
		ret+=s[x];
		x=x-lowbit(x);
	}
	return ret;
}

void updata(int x,int y){
	while (x <= n+1){
		s[x]+=y;
		x=x+lowbit(x);
	}
}

bool cmp(DD & xx,DD & yy){
	if (xx.v == yy.v) return xx.num<yy.num;
	else return xx.v<yy.v;
}

int main(){
	char c[N];
	int T,i,j;
	LL ans;
	scanf("%d",&T);
	while (T--){
		scanf("%s",c);
		n=strlen(c);
		Manacher(c,n);
		for (i=1; i<=n; i++){
			Mp[i]=Mp[i*2]/2-1;
		//	printf("%d ",Mp[i]);
		}
		//printf("\n");
		memset(s,0,sizeof(s));
		ans=0;
		DD a[N];
		for (i=1; i<=n; i++){
			j=i-Mp[i];
			a[i].num=i;
			a[i].v=j;
			//updata(i,1);
		}
		sort(a+1,a+1+n,cmp);
		j=1;
		for (i=1; i<=n; i++){
			while (a[j].v<=i && j<=n){
				updata(a[j].num,1);
				j++;
			}
			if (Mp[i] < 1) continue;
			ans+=(getsum(i+Mp[i])-getsum(i));
		}
/*		for (i=1; i<=n; i++){
			updata(i-Mp[i],1);
		}
		int sum[N];
		ans=0;
		memset(sum,0,sizeof(sum));
		for (i=1; i<=n; i++){
			sum[i+Mp[i]]++;
		}
		for (i=n; i>0; i--){
			updata(i-Mp[i],-1);
			printf("%d %d\n",sum[i],getsum(i));
			if (sum[i] > 1) ans+=(LL)(sum[i]-1)*(LL)getsum(i-Mp[i]);
		}*/
		printf("%lld\n",ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值