线段树+Hash
听说这方法比不过暴力?(雾)
解题过程
首先考虑第二个问题,如何快速求解一个子串是不是另一个串的循环节。
此处有一个结论:
假设 S 为 的长度为 n 。
只要判断 S[1…n-d+1] 和 S[d+1…n] 是否相等即可。
简单的想,假设当前循环节是(k),那么就有:
(a)
(a*ch+b)
(a*ch^2+b*ch+c)
(a*ch^3+b*ch^2+c*ch+d)
(a*ch^4+b*ch^3+c*ch^2+d*ch+e)
(a*ch^5+b*ch^4+c*ch^3+d*ch^2+e*ch+f)
必从(k)个字母循环,假设(k=2).那么就有:
(a)
(a*ch+b)
(a*ch^2+b*ch+a)
(a*ch^3+b*ch^2+a*ch+b)
(a*ch^4+b*ch^3+a*ch^2+b*ch+a)
(a*ch^5+b*ch^4+a*ch^3+b*ch^2+a*ch+b)
发现前面两个和后面两个Hash值相等,再通过数学归纳法,可以证明。
其实也很好想,因为我们要通过Hash[n]-Hash[n-d]相减来判断是不是s[0]-s[d]的多少(ch)倍,必须满足后面几个相等,因为只有是循环节,才能将前面的抵消掉。
第一个问题,如何处理赋值与Hash值之间的关系
一段的一段的赋值,查找也是一段一段的,很容易可以想到用线段树来维护一个Hash值。
每个点存当前的Hash值,但是这里就有一个问题,因为是存的从(ch^0)开始的,那么怎么才能转移到父亲节点呢?
kano[x].h=((kano[x<<1].h*Pow[kano[x<<1|1].rkano[x<<1|1].l+1]+kano[x<<1|1].h)%mod+mod)%mod;
左儿子里的值和他在父亲节点应当是的值差了什么呢?
根据Hash函数的定义:(Hash(l,r)=Hash[r]-Hash[l-1]*p^{r-l+1})
所以说,左儿子里的值就差了乘上一个(ch^{sizeof(rightson)})
这个问题解决了,那(pushdown)又该如何呢?
void pushdown(int x){
if(kano[x].lazy+1){
kano[x<<1].lazy=kano[x<<1|1].lazy=kano[x].lazy;
kano[x<<1].h=Pow2[kano[x<<1].r-kano[x<<1].l]*kano[x].lazy%mod;
kano[x<<1|1].h=Pow2[kano[x<<1|1].r-kano[x<<1|1].l]*kano[x].lazy%mod;
kano[x].lazy=-1;
}
return ;
}
(lazy)里存的是将要变成的值,那么如何将这些值赋到儿子中呢?
可以看,在没有赋值前假设左儿子的值是:
(x*ch^3+y*ch^2+z*ch),
那么赋值之后就应该是:(k*ch^3+k*ch^2+k*ch).
可以发现,就是在(k)的基础上(*)了一个(ch^3+ch^2+ch),因此就可以同时维护一个(ch^x+xh^{x-1}+ch^{x-2}+……ch)的一个东西,就可以快速地(pushdown)。
注意事项
1.有递归操作时,一定都下传(lazy)。
2.注意边界。
3.我每次修改时都将k加了1,防止0,(lazy)为-1,防止0的出现。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int b=17;
const int N=1e5+50;
const int mod=1e9+9;
typedef unsigned long long ULL;
char s[N];
int n,m,opt,k,l,r;
ULL Pow[N],Pow2[N];
struct Kano{
int l,r,lazy;
ULL h;
}kano[N*6];
void pushup(int x){
kano[x].h=((kano[x<<1].h*Pow[kano[x<<1|1].r-kano[x<<1|1].l+1]+kano[x<<1|1].h)%mod+mod)%mod;
}
void pushdown(int x){
if(kano[x].lazy+1){
kano[x<<1].lazy=kano[x<<1|1].lazy=kano[x].lazy;
kano[x<<1].h=Pow2[kano[x<<1].r-kano[x<<1].l]*kano[x].lazy%mod;
kano[x<<1|1].h=Pow2[kano[x<<1|1].r-kano[x<<1|1].l]*kano[x].lazy%mod;
kano[x].lazy=-1;
}
return ;
}
void build(int x,int l,int r){
kano[x].l=l;kano[x].r=r;kano[x].lazy=-1;
if(l==r){
kano[x].h=s[l]-'0'+1;
return;
}
int mid=(kano[x].l+kano[x].r)>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
pushup(x);
}
void add(int x,int l,int r,int a){
if(kano[x].l==l&&kano[x].r==r){
kano[x].lazy=a;
kano[x].h=Pow2[kano[x].r-kano[x].l]*a%mod;
return;
}
pushdown(x);
int mid=(kano[x].l+kano[x].r)>>1;
if(mid>=r) add(x<<1,l,r,a);
else if(mid<l) add(x<<1|1,l,r,a);
else add(x<<1,l,mid,a),add(x<<1|1,mid+1,r,a);
pushup(x);
}
ULL query(int x,int l,int r){
if(l>r) return 0;
if(kano[x].l==l&&kano[x].r==r)
return kano[x].h;
pushdown(x);
int mid=(kano[x].l+kano[x].r)>>1;
if(mid>=r) return query(x<<1,l,r);
else if(mid<l) return query(x<<1|1,l,r);
else return (query(x<<1,l,mid)*Pow[r-mid]+query(x<<1|1,mid+1,r))%mod;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
m+=k;
scanf("%s",s+1);
Pow[0]=Pow2[0]=1;
for(int i=1;i<=n;i++) Pow[i]=Pow[i-1]*b%mod,Pow2[i]=(Pow2[i-1]*b+1)%mod;
build(1,1,n);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&opt,&l,&r,&k);
if(opt==1) add(1,l,r,k+1);
else printf("%s
",query(1,l,r-k)==query(1,l+k,r)?"YES":"NO");
}
}
听说这东西打起来很爽,调起来更爽.(雾)