Subpalindromes URAL - 1989 (字符串hash+线段树)

题目链接

题意:给定一个小写字母组成的字符串,m次操作,每次修改单个位置的字符或者查询[l,r]这段子串是否是回文串,给出Yes/No

思路:输入字符串s0,设字符串的逆序串为s1。

如果这题没有单点修改的话,直接对s0 s1求出hash数组,然后对于[l,r]判断左边区间的s0的hash值是否等于右半区间的s1的hash值 。

但是涉及单点修改的话,就需要线段树维护区间hash值。

而且需要两棵线段树对s0 s1分别维护。

hash的区间合并:
在这里插入图片描述

另外还有一些细节,比如查询时的写法(返回结构体)
见代码注释

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long 
const int maxn = 1e5+7;
const ll inf = 34359738370;
const int has = 99959;
//给定字符串 m次操作 修改单个字符 或者查询[l,r]子串是否是回文串 
//思路:开2个线段树分别维护左到右的区间hash值 右到左的区间hash值 每次单点修改
ull p[maxn];//p p^2 p^3...
int n,m;
char s[2][maxn];
struct segt
{
    int l,r;
    ull v;
    segt operator + (const segt &a)const //重载+ 变成区间合并
    {
        segt res;
        res.l=l,res.r=a.r;
        res.v=v*p[(a.r-a.l+1)]+a.v;
        return res;
    }
}tree[2][maxn<<2];
inline int lc(int &rt){ return rt<<1 ; }
inline int rc(int &rt){ return rt<<1|1 ; }
inline void pushup(int &rt,int k){ tree[k][rt] = tree[k][lc(rt)] + tree[k][rc(rt)] ; }
inline void build(int rt,int l,int r,int k)
{
    tree[k][rt].l=r,tree[k][rt].r=r;
    if(l == r)
    {
        tree[k][rt].v=s[k][l]-'a'+1;
        return ;
    }
    int mid=(l+r)>>1;
    build(lc(rt),l,mid,k);
    build(rc(rt),mid+1,r,k);
    pushup(rt,k);
}
inline void updata(int rt,int x,ull v,int k)
{
    if(tree[k][rt].l == tree[k][rt].r)
    {
        tree[k][rt].v=v;
        return ;
    }
    int mid=(tree[k][rt].l + tree[k][rt].r)>>1;
    if(x<=mid) updata(lc(rt),x,v,k);
    else updata(rc(rt),x,v,k);
    pushup(rt,k);
}
inline segt query(int rt,int l,int r,int vl,int vr,int k)
{
	//这里用带4个return的这种写法比较简单  因为涉及区间的特殊合并
    if(vl<=l && r<=vr) return tree[k][rt];
    int mid=(l+r)>>1;
    if(vl>mid) return query(rc(rt),mid+1,r,vl,vr,k);
    else if(vr<=mid) return query(lc(rt),l,mid,vl,vr,k);
    return query(lc(rt),l,mid,vl,vr,k)+query(rc(rt),mid+1,r,vl,vr,k);
}
int main()
{
    p[1]=has;
    for(int i=2;i<=maxn;i++) p[i]=p[i-1]*has;
    while(~scanf("%s",s[0]+1)){
        n=strlen(s[0]+1);
        for(int i=1;i<=n;i++) s[1][i]=s[0][n-i+1];
        scanf("%d",&m);
        char q[100],ch[2];
        int a,b;
        build(1,1,n,0);
        build(1,1,n,1);
        while(m--)
        {
            scanf("%s",q);
            if(q[0]=='c')
            {
                scanf("%d %s",&a,ch);
                ull v=ch[0]-'a'+1;
                updata(1,a,v,0);
                updata(1,n-a+1,v,1);
            }
            else if(q[0]=='p')
            {
                scanf("%d %d",&a,&b);
                if(a==b)
                {
                    puts("Yes");
                    continue;
                }
                int mid=(a+b)>>1;
                int r=mid;
                if((b-a)%2==0) r=mid-1;//奇数长度 左边区间舍弃中间那个字符
                ull t1=query(1,1,n,a,r,0).v,t2=query(1,1,n,n-b+1,n-mid,1).v;
                if(t1 == t2)puts("Yes");
                else puts("No");
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值