Subpalindromes URAL - 1989(树状数组+哈希)

6 篇文章 0 订阅
2 篇文章 0 订阅

You have a string and queries of two types:
replace i’th character of the string by character a;
check if substring sj…sk is a palindrome.
Input
The first line contains a string consisting of n small English letters. The second line contains an integer m that is the number of queries (5 ≤ n, m ≤ 10 5). The next m lines contain the queries.
Each query has either form “change i a”, or “palindrome? j k”, where i, j, k are integers (1 ≤ i ≤ n; 1 ≤ j ≤ k ≤ n), and character a is a small English letter.
Output
To all second type queries, you should output “Yes” on a single line if substring s j… s k is a palindrome and “No” otherwise.
Example
input output
abcda
5
palindrome? 1 5
palindrome? 1 1
change 4 b
palindrome? 1 5
palindrome? 2 4
No
Yes
Yes
Yes

题意:给定一个字符串s,有两种操作,第一种操作:palindrome? l r,询问s[l] s[l+1] …s[r] 是不是回文串,如果是输出“Yes”,否则输出“No”,第二种操作:change ind val,将下标ind位置的字符改为val。

思路:如果直接暴力的做,每次求出l-r和r-l的字符串的哈希值,复杂度为O(n^2),题目时限为500ms,很显然这样会超时,所以需要一种数据结构来保存字符串的哈希值。
首先我们看看暴力做的时候的复杂度,更新值的复杂度为O(1),计算哈希值的复杂度为O(n)。所以我们需要优化的是计算哈希值的复杂度,很显然树状数组或者线段树可以解决这个问题,求值只需要O(logn)的复杂度,而更新值的复杂度也是O(logn),总复杂度就是O(nlogn),很显然这个优化方法是可行的。

代码:

#include<bits/stdc++.h>
#define LL long long
#define Max 100005
#define ULL unsigned long long
const LL mod=1e9+7;
const ULL base=131;
const LL LL_MAX=9223372036854775807;
using namespace std;
ULL c[2*Max][2],p[Max];//C[..][0]保存左到右的哈希值
//C[..][1]保存右到左的哈希值
char s[Max],opt[Max];
int len,n;
void init()
{
    p[0]=1;
    for(int i=1;i<Max;i++)
        p[i]=p[i-1]*base;
}
int lowbit(int x)
{
    return x&(-x);
}
ULL getsum(int x,int dir)
{
    ULL ans=0;
    for(int i=x;i>0;i-=lowbit(i))
        ans+=c[i][dir];
    return ans;
}
void add(int x,ULL val,int dir)
{
    for(int i=x;i<=len;i+=lowbit(i)){
        c[i][dir]+=val;
    }
}
int main()
{
    scanf("%s",s+1);
    len=strlen(s+1);
    init();
    for(int i=1;i<=len;i++){
        add(i,s[i]*p[len-i],0);
        add(i,s[len-i+1]*p[len-i],1);
    }
    scanf("%d",&n);
    while(n--){
        scanf("%s",opt);
        if(opt[0]=='p'){
            ULL h1,h2;
            int l,r;
            scanf("%d%d",&l,&r);
            h1=(getsum(r,0)-getsum(l-1,0))*p[l-1];
            h2=(getsum(len-l+1,1)-getsum(len-r,1))*p[len-r];
            /*
            这里需要注意,拿abcda为例子
            左到右哈希值为:
            a*p^4+b*p^3+c*p^2+d*p+a
            右到左的哈希值为:
            a*p^4+d*p^3+c*p^2+b*p+a
            如果你要求3-4是不是回文串,比较的值为:
            c*p^2+d*p 与 d*p^3+c*p^2
            很显然它们之间的次方不对需要乘上。
            */
            if(h1==h2)
                printf("Yes\n");
            else
                printf("No\n");
        }
        else{
            int ind;
            char val[2];
            scanf("%d%s",&ind,val);
            ULL t=val[0]-s[ind];
            s[ind]=val[0];
            add(ind,t*p[len-ind],0);
            add(len-ind+1,t*p[ind-1],1);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值