牛客小白月初赛38-J科学幻想(线段树维护字符串哈希+二分)
Posted zjj0624
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客小白月初赛38-J科学幻想(线段树维护字符串哈希+二分)相关的知识,希望对你有一定的参考价值。
题意
定义一种关系,如果两个字符串只有一个位置字母不同,也算相等。
给你一个字符串,长度为n,有k次操作,第一种操作是改变字符串中的一个字母,第二个操作是查询两个区间是否相等。
思路
我们知道如果没有修改操作,只有查询操作,我们可以用字符串hash+二分来解决问题,因为只有一个字母不相等的时候才算相等,我们可以通过判断
[
l
1
,
m
i
d
1
]
,
[
l
2
,
m
i
d
2
]
[l1,mid1],[l2,mid2]
[l1,mid1],[l2,mid2]是否相等,如果相等就说明不同的字母在右区间,就让
m
i
d
=
l
+
1
mid=l+1
mid=l+1,通过这种方式就可以判断两个区间是否相等了,但是如果加上修改操作的话,我们可以用线段树来维护这个字符串hash,这样就可以满足条件了。时间复杂度
O
(
n
×
l
o
g
(
n
)
+
m
×
l
o
g
(
n
)
)
O(n\\times log(n)+m\\times log(n) )
O(n×log(n)+m×log(n))
代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define fi first
#define se second
#define pb push_back
#define me memset
const int N = 1e5+10;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
struct Node
{
int l,r;
ull v;
}tr[N*4];
ull f[N];
const ll base=23333;
char s[N];
int n,k;
void push_up(int u)
{
int mid=tr[u].l+tr[u].r>>1;
tr[u].v=tr[u<<1].v*f[tr[u].r-mid]+tr[u<<1|1].v;
}
void build(int u,int l,int r)
{
tr[u].l=l,tr[u].r=r;
if(l==r)
{
tr[u].v=s[l]-'a';
return ;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int pos,char x)
{
if(l==r)
{
tr[u].v=x-'a';
return ;
}
else
{
int mid=l+r>>1;
if(pos<=mid) update(u<<1,l,mid,pos,x);
if(mid<pos) update(u<<1|1,mid+1,r,pos,x);
push_up(u);
}
}
ull query(int u,int l,int r,int L,int R)
{
//cout<<l<<" "<<r<<endl;
ull ans=0;
if(l>=L&&r<=R)
{
//cout<<u<<" "<<tr[u].v<<" "<<f[R-tr[u].r]<<endl;
return tr[u].v*f[R-tr[u].r];
}
int mid=l+r>>1;
if(L>mid) ans+=query(u<<1|1,mid+1,r,L,R);
else if(mid>=R) ans+=query(u<<1,l,mid,L,R);
return ans;
}
int main()
{
scanf("%d%d",&n,&k);
scanf("%s",s+1);
f[0]=1;
for(int i=1 ; i<=n ; i++) f[i]=f[i-1]*base;
build(1,1,n);
while(k--)
{
int op,l1,r1,l2,r2;
scanf("%d",&op);
if(op==1)
{
int x;
char y;
scanf("%d %c",&x,&y);
update(1,1,n,x,y);
}
else
{
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(r1-l1!=r2-l2)
{
printf("NO\\n");
continue;
}
ull v1=query(1,1,n,l1,r1);
ull v2=query(1,1,n,l2,r2);
if(v1==v2)
{
printf("YES\\n");
continue;
}
bool flag=true;
while(l1<r1)
{
//cout<<l1<<" "<<r1<<endl;
int mid1=l1+r1>>1;
int mid2=l2+r2>>1;
ull v_l1=query(1,1,n,l1,mid1);
ull v_r1=query(1,1,n,mid1+1,r1);
ull v_l2=query(1,1,n,l2,mid2);
ull v_r2=query(1,1,n,mid2+1,r2);
if(v_l1!=v_l2&&v_r1!=v_r2)
{
flag=false;
break;
}
else if(v_l1==v_l2&&v_r1!=v_r2)
{
l1=mid1+1;
l2=mid2+1;
}
else
{
r1=mid1;
r2=mid2;
}
}
if(flag) printf("YES\\n");
else printf("NO\\n");
}
}
return 0;
}
/*
5 4
abcde
1 4 a
1 5 b
2 1 2 4 5
*/
总结
这一题中我知道区间的字符串哈希是怎么转移的。
h
a
s
h
(
i
,
j
)
=
h
a
s
h
[
i
,
k
]
∗
s
e
e
d
(
j
−
k
+
1
)
+
h
a
s
h
[
k
,
j
]
;
hash(i,j)=hash[i,k]∗seed(j−k+1)+hash[k,j];
hash(i,j)=hash[i,k]∗seed(j−k+1)+hash[k,j];
线段树的转移代码
tr[u].v=tr[u<<1].v*f[tr[u].r-mid]+tr[u<<1|1].v;
以上是关于牛客小白月初赛38-J科学幻想(线段树维护字符串哈希+二分)的主要内容,如果未能解决你的问题,请参考以下文章
Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵