51Nod1553 周期串查询 字符串 哈希 线段树

原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1553.html

题目传送门 - 51Nod1553

题意

  有一个串只包含数字字符。串的长度为n,下标从1开始。

  有两种操作方式:

  1 l r c (1≤l≤r≤n, c是数字字符),表示将l到r这一段的字符全部改成c字符;

  2 l r d (1≤l≤r≤n, 1≤d≤r-l+1),表示询问l到r这一段区间内的子串是否是以d为周期的串。

  字符串s是以x (1≤x≤|s|),为周期的串的条件是:对于所有的 i从1到|s|-x, $s_i = s_{i + x}$ 都成立。

  $n\leq 10^5$ ,操作总数 $\leq 2\times 10^5$ 。

题解

  只要 $s[l\cdots r-d] = s[l+d\cdots r]$ 那么就是周期串。

  直接线段树维护哈希值就好了。

代码

#include <bits/stdc++.h>
using namespace std;
int read(){
	int x=0;
	char ch=getchar();
	while (!isdigit(ch))
		ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=100005;
int n,m;
int a[N];
struct HashSeg{
	int mod,P,Pow[N],sp[N];
	int t[N<<2],sz[N<<2],add[N<<2];
	void build(int rt,int L,int R){
		sz[rt]=R-L+1,add[rt]=-1;
		if (L==R){
			t[rt]=a[L];
			return;
		}
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		build(ls,L,mid);
		build(rs,mid+1,R);
		t[rt]=(1LL*t[ls]*Pow[sz[rs]]+t[rs])%mod;
	}
	void init(int _mod,int _P){
		mod=_mod,P=_P;
		Pow[0]=1;
		for (int i=1;i<=n;i++)
			Pow[i]=1LL*Pow[i-1]*P%mod;
		sp[0]=0;
		for (int i=1;i<=n;i++)
			sp[i]=(sp[i-1]+Pow[i-1])%mod;
		build(1,1,n);
	}
	void cover(int rt,int d){
		t[rt]=1LL*d*sp[sz[rt]]%mod;
		add[rt]=d;
	}
	void pushdown(int rt){
		int &v=add[rt];
		if (!~v)
			return;
		cover(rt<<1,v);
		cover(rt<<1|1,v);
		v=-1;
	}
	void update(int rt,int L,int R,int xL,int xR,int d){
		if (R<xL||L>xR)
			return;
		if (xL<=L&&R<=xR)
			return cover(rt,d);
		pushdown(rt);
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		update(ls,L,mid,xL,xR,d);
		update(rs,mid+1,R,xL,xR,d);
		t[rt]=(1LL*t[ls]*Pow[sz[rs]]+t[rs])%mod;
	}
	int query(int rt,int L,int R,int xL,int xR){
		if (R<xL||L>xR)
			return 0;
		if (xL<=L&&R<=xR)
			return 1LL*t[rt]*Pow[xR-R]%mod;
		pushdown(rt);
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		return (query(ls,L,mid,xL,xR)+query(rs,mid+1,R,xL,xR))%mod;
	}
}T0,T1;
int main(){
	n=read(),m=read()+read();
	for (int i=1;i<=n;i++)
		scanf("%1d",&a[i]);
	T0.init(998244353,233);
	T1.init(1e9+7,101);
	while (m--){
		int opt=read(),L=read(),R=read(),d=read();
		if (opt==1){
			T0.update(1,1,n,L,R,d);
			T1.update(1,1,n,L,R,d);
		}
		else {
			if (R-L+1<=d)
				puts("YES");
			else if (T0.query(1,1,n,L,R-d)==T0.query(1,1,n,L+d,R))
				   &&T1.query(1,1,n,L,R-d)==T1.query(1,1,n,L+d,R))
				puts("YES");
			else
				puts("NO");
		}
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/zhouzhendong/p/51Nod1553.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值