URAL 1989. Subpalindromes

原题连接:http://blog.csdn.net/zearot/article/details/38921403



题目大意:给定一个字符串,然后有两个操作:

                一为修改这个字符串中的任意一个字符,    

                二为选择其中一个子串,从j到k,判断str[j.....k]这个子串是不是回文串。


题解:这道不能直接用暴力判断子串是不是回文,这样会超时的,要用线段树和多项式哈希字符串来维护。

对于任意一个字符串,可以定义它的哈希值,因此可以从左边开始也可以从右边开始。对于从l到r的一段字符串,有

hashl = str[l] + str[l+1]*k + str[l+2]*k^2 + str[l+3]*k^3 + ……+str[r]*k^(r-l)

hashr = str[r] + str[r+1]*k + str[r+2]*k^2 + str[r+3]*k^3 + ……+str[l]*k^(r-l)

对于这个从l到r的字符串,只要有hashl == hashr,则该字符串为回文串,故只要用线段树维护hashl和hashr便可。


代码如下:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100000+100;
int M,len;
char str[maxn];
int pp[maxn];
struct node{
	int hashl,hashr;
}p[maxn<<2];

void PushUp(int ll, int rr, int rt){	//ll表示节点左子树区间个数,rr表示右子树区间个数,rt表示当前节点编号 
	p[rt].hashl = p[rt<<1].hashl + p[rt<<1|1].hashl*pp[ll];
	p[rt].hashr = p[rt<<1].hashr*pp[rr] + p[rt<<1|1].hashr;
}

//Build函数建树 
void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号
	if(l==r) {//若到达叶节点 
		p[rt].hashl = p[rt].hashr = str[l];
		return;
	}
	int m=(l+r)>>1;
	//左右递归 
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	//更新信息 
	PushUp(m-l+1, r-m, rt);
}

void Update(int L,char C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号
	if(l==r){//到叶节点,修改 
		p[rt].hashl = p[rt].hashr = C;
		return;
	}
	int m=(l+r)>>1;
	//根据条件判断往左子树调用还是往右 
	if(L <= m) Update(L,C,l,m,rt<<1);
	if(L > m)  Update(L,C,m+1,r,rt<<1|1);
	PushUp(m-l+1, r-m, rt);//子节点更新了,所以本节点也需要更新信息 
} 

node Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号
	if(L <= l && r <= R){
		//在区间内,直接返回 
		return p[rt];
	}
	int m=(l+r)>>1;
	
	node ANS,ANS1,ANS2;
	int flag1 = 0, flag2 = 0;
	if(L <= m) ANS1 = Query(L,R,l,m,rt<<1), flag1 = 1;
	if(R >  m) ANS2 = Query(L,R,m+1,r,rt<<1|1), flag2 = 1;
	if(flag1 && !flag2) ANS = ANS1;
	else if(!flag1 && flag2) ANS = ANS2;
	else if(flag1 && flag2) {
		ANS.hashl = ANS1.hashl + ANS2.hashl*pp[m - max(L,l) + 1];
		ANS.hashr = ANS1.hashr*pp[min(r,R) - m] + ANS2.hashr; 
	}
	return ANS;
} 




int main(){
	scanf("%s",str+1);
	str[0]='0'; pp[0]=1;
	len = strlen(str)-1;
	memset(p,0,sizeof(p));
	for(int i=1;i<=len;i++){
		pp[i] = pp[i-1] * 137;
	}
	scanf("%d",&M);
	Build(1, len, 1);
	for(int i=1;i<=M;i++){
		char s[25];
		scanf("%s",s);
		if(s[0] == 'c'){
			int a; char b;
			scanf("%d %c",&a,&b);
			Update(a, b, 1, len, 1);
		}
		else {
			int a, b;
			scanf("%d%d",&a,&b);
			node ans = Query(a, b, 1, len, 1);
			if(ans.hashl == ans.hashr) printf("Yes\n");
			else printf("No\n");
		}
	}
			
} 





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值