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 "); 120 else printf("No "); 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 }