ural 1989 subplindromes

https://vjudge.net/problem/URAL-1989

题意:

先给出一个字符串,对于这个字符串,有两种操作,一种是询问从下标x到y的串是不是回文串,另一种是将下标为pos的字符改为另一种字符。

思路:

哎,看题解补的,还好学会了如何用hash判断回文串以及线段树单点更新在hash中的应用。

下面来详细讲讲吧。

首先,对于一个字符串,一共出现过n个不同的字符,那么就可以把这个字符串用n+1进制表示(考虑特殊元素0,比如bbaa,如果用26进制的话,那么就是1100,就会跟bb00起冲突,这一点要牢记)。因为这个n+1进制数表示为10进制数可能会很大,考虑无符号整型数,让其自然溢出,冲突的概率可以忽略不计(道听途说,这题反正没有问题)。

在这题中,随时需要计算某个串的10进制表示,所以就需要把27^0,27^1,27^2……预处理出来,在计算的时候就是O(1)的复杂度。

这题既然询问的是回文串,那么正向hash和反向hash都需要计算。之后,就用线段树来计算每一段字符串的hash值。当线段树建树的时候,递归到左右下标相等,那么此时就可以计算这个字符例如b的双向hash值,比如是字符串的长度是9,它的下标是3(注意字符串的下标从0开始,而线段树的最小下标是从1开始的),那么它的正向hash值就是000b,即为3 * (27) ^ 3,反向hash值就是00000b,即为 3 * (27) ^ 5。除了非真子树之外的其它节点的左hash,等于它的左儿子的左hash的值与右儿子的左hash的值的和,右hash类似。这样数就算建好了。

之后我们查询的时候,按照线段树的方式来查询就好了,不过有一个要注意的地方,举一个例子,字符串的长度为8,abcdasde,查询的是2到5,即为bcda是否为回文串,但是此时通过查询得到的左hash值是0bcda,右hash值是000adcb,所以他们的位数实际是不相等的,这里我们就需要做进一步的处理使得他们的位数相等才能做比较,这时候需要把0bcda,向右移两位,变成000bcda,如何移位呢,这里其实跟2进制的位运算有异曲同工之妙的,直接乘27^(相差的0的个数),实际就是x-1和n-y的差的绝对值了。(这里自己举个例子就很明显了。)之后再比较左hash和右hash就ok了。

更新的操作大概是这里面最简单的吧,不过不要忘记了向上更新的函数。

代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 
  4 #define N 100005
  5 #define ll unsigned long long
  6 
  7 ll f[N];
  8 int n;
  9 
 10 struct tree
 11 {
 12     int l,r;
 13     ll suml,sumr;
 14 } tree[N << 2];
 15 
 16 char s[N];
 17 
 18 void pushup(int o)
 19 {
 20     tree[o].suml = tree[o << 1].suml + tree[o << 1|1].suml;
 21     tree[o].sumr = tree[o << 1].sumr + tree[o << 1|1].sumr;
 22 }
 23 
 24 void build(int o,int l,int r)
 25 {
 26     tree[o].l = l;
 27     tree[o].r = r;
 28 
 29     if (l == r)
 30     {
 31         tree[o].suml = f[l-1] * (s[l-1] - 'a');
 32         tree[o].sumr = f[n-l] * (s[l-1] - 'a');
 33         return;
 34     }
 35 
 36     int m = (l + r) >> 1;
 37 
 38     build(o << 1,l,m);
 39     build(o << 1 | 1,m+1,r);
 40 
 41     pushup(o);
 42 }
 43 
 44 ll suml,sumr;
 45 
 46 void query(int o,int l,int r)
 47 {
 48     if (tree[o].l >= l && tree[o].r <= r)
 49     {
 50         suml += tree[o].suml;
 51         sumr += tree[o].sumr;
 52 
 53         return;
 54     }
 55 
 56     int m = (tree[o].l + tree[o].r) >> 1;
 57 
 58     if (m >= l) query(o << 1,l,r);
 59     if (m < r) query(o << 1|1,l,r);
 60 }
 61 
 62 void update(int o,int pos,int c)
 63 {
 64     if (tree[o].l == tree[o].r)
 65     {
 66         tree[o].suml = f[pos-1] * c;
 67         tree[o].sumr = f[n-pos] * c;
 68 
 69         return;
 70     }
 71 
 72     int m = (tree[o].l + tree[o].r) >> 1;
 73 
 74     if (pos <= m) update(o << 1,pos,c);
 75     if (pos > m) update(o << 1|1,pos,c);
 76 
 77     pushup(o);
 78 }
 79 
 80 int main()
 81 {
 82     f[0] = 1;
 83 
 84     for (int i = 1;i < N;i++)
 85         f[i] = f[i-1] * 27;
 86 
 87     scanf("%s",s);
 88 
 89     n = strlen(s);
 90 
 91     build(1,1,n);
 92 
 93     int num;
 94 
 95     scanf("%d",&num);
 96 
 97     for (int i = 0;i < num;i++)
 98     {
 99         char a[50];
100 
101         scanf("%s",a);
102 
103         if (a[0] == 'p')
104         {
105             suml = sumr = 0;
106 
107             int x,y;
108 
109             scanf("%d%d",&x,&y);
110 
111             query(1,x,y);
112 
113             int d1 = x - 1;
114             int d2 = n - y;
115 
116             if (d1 > d2) sumr *= f[d1-d2];
117             else suml *= f[d2-d1];
118 
119             if (sumr == suml) printf("Yes\n");
120             else printf("No\n");
121         }
122         else
123         {
124             char cc[10];
125 
126             int pos;
127 
128             scanf("%d%s",&pos,cc);
129 
130             update(1,pos,cc[0] - 'a');
131         }
132     }
133 
134     return 0;
135 }

 

posted @ 2017-07-27 21:51  qrfkickit  阅读(277)  评论(0编辑  收藏  举报