牛客小白月初赛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(jk+1)+hash[k,j];

线段树的转移代码

tr[u].v=tr[u<<1].v*f[tr[u].r-mid]+tr[u<<1|1].v;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值