【线段树哈希】「Balkan OI 2016」Haker
1A海星
题目大意
给你一个长度为 $n$ ,由小写字母构成的字符串 $S$ 和 $Q$ 个操作,每个操作是以下 3 种之一:
- 1 x y k :询问当前字符串从位置 $x$ 到 $y$ 的子串与从位置 $k$ 开始,长度为 $y-x+1$ 的子串是否相同。
- 2 x y k :将当前字符串从位置 $x$ 到 $y$ 的子串变成原始串从位置 $k$ 开始,长度为 $y−x+1$ 的子串。
- 3 x y :将当前字符串从位置 $x$ 到 $y$ 的子串中的所有 $a$ 变成 $b$ ,$b$ 变成 $c$ ,$\cdots $ ,$z$ 变成 $a$ 。
$n,q \le 10^5$
题目分析
初一看前两个操作用字符串哈希都可以轻松解决,比较难处理的是这个操作3循环位移。
如果一直只想着对字符按顺序哈希这一种狭隘的哈希,这个循环位移是没法处理的。那么从其他角度考虑,由于循环位移涉及到两个不同质字符的 交换,所以应该记录一些只关于一种同质字符的信息——它的出现位置。也就是说对它的出现位置哈希,从而实现循环位移,而比较相同只需要将$|\sum|$个字符的出现位置都比较一次就可以了。
对出现位置哈希还有一个好处就是可以处理 形式相同(即按最小表示法相同)的字符串比较。
这题另外一种变式就是把操作2改成:“将当前字符串从位置 $x$ 到 $y$ 的子串变成 当前串 从位置 $k$ 开始,长度为 $y−x+1$ 的子串”。那这个相当于是覆盖为历史版本的线段树上哈希值,可持久化一下应该就可以了。
只写了最简单的原题版本。
1 #include<bits/stdc++.h> 2 typedef unsigned int uint; 3 const int maxn = 100035; 4 const uint base = 2333; 5 6 struct node 7 { 8 uint val[31]; 9 int tag,rnd; 10 }f[maxn<<2]; 11 int n,m; 12 char s[maxn]; 13 uint hsh[maxn][31],pwr[maxn],retx[31],rety[31]; 14 15 inline char nc(){ 16 static char buf[100000],*p1=buf,*p2=buf; 17 return p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 18 } 19 #define getchar nc 20 int read() 21 { 22 char ch = getchar(); 23 int num = 0, fl = 1; 24 for (; !isdigit(ch); ch=getchar()) 25 if (ch=='-') fl = -1; 26 for (; isdigit(ch); ch=getchar()) 27 num = (num<<1)+(num<<3)+ch-48; 28 return num*fl; 29 } 30 uint hash(int i, int l, int r) 31 { 32 return hsh[r][i]-hsh[l-1][i]*pwr[r-l+1]; 33 } 34 void build(int rt, int l, int r) 35 { 36 f[rt].tag = -1, f[rt].rnd = 0; 37 for (int i=1; i<=26; i++) f[rt].val[i] = hash(i, l, r); 38 if (l==r) return; 39 int mid = (l+r)>>1; 40 build(rt<<1, l, mid); 41 build(rt<<1|1, mid+1, r); 42 } 43 void round(int rt) 44 { 45 for (int i=27; i>=2; i--) 46 f[rt].val[i] = f[rt].val[i-1]; 47 f[rt].val[1] = f[rt].val[27]; 48 } 49 void pushdown(int rt, int l, int mid, int r) 50 { 51 if (f[rt].tag!=-1){ 52 f[rt<<1].tag = f[rt].tag, f[rt<<1|1].tag = f[rt].tag+mid-l+1, f[rt].tag = -1; 53 f[rt<<1].rnd = 0, f[rt<<1|1].rnd = 0; 54 for (int i=1; i<=26; i++) 55 f[rt<<1].val[i] = hash(i, f[rt<<1].tag, f[rt<<1].tag+mid-l), 56 f[rt<<1|1].val[i] = hash(i, f[rt<<1|1].tag, f[rt<<1|1].tag+r-mid-1); 57 } 58 f[rt<<1].rnd += f[rt].rnd, f[rt<<1|1].rnd += f[rt].rnd; 59 for (int i=1; i<=f[rt].rnd; i++) round(rt<<1), round(rt<<1|1); 60 f[rt].rnd = 0; 61 62 } 63 void query(int rt, int l, int r, int L, int R, uint *ret) 64 { 65 if (L <= l&&r <= R){ 66 for (int i=1; i<=26; i++) ret[i] = ret[i]*pwr[r-l+1]+f[rt].val[i]; 67 }else{ 68 int mid = (l+r)>>1; 69 pushdown(rt, l, mid, r); 70 if (L <= mid) query(rt<<1, l, mid, L, R, ret); 71 if (R > mid) query(rt<<1|1, mid+1, r, L, R, ret); 72 } 73 } 74 void addtag(int rt, int l, int r, int L, int R, int k) 75 { 76 if (L <= l&&r <= R){ 77 f[rt].tag = k+l-L, f[rt].rnd = 0; 78 for (int i=1; i<=26; i++) 79 f[rt].val[i] = hash(i, k+l-L, k+r-L); 80 }else{ 81 int mid = (l+r)>>1; 82 pushdown(rt, l, mid, r); 83 if (L <= mid) addtag(rt<<1, l, mid, L, R, k); 84 if (R > mid) addtag(rt<<1|1, mid+1, r, L, R, k); 85 } 86 } 87 void circulation(int rt, int l, int r, int L, int R) 88 { 89 if (L <= l&&r <= R) ++f[rt].rnd, round(rt); 90 else{ 91 int mid = (l+r)>>1; 92 pushdown(rt, l, mid, r); 93 if (L <= mid) circulation(rt<<1, l, mid, L, R); 94 if (R > mid) circulation(rt<<1|1, mid+1, r, L, R); 95 } 96 } 97 int main() 98 { 99 scanf("%s",s+1); 100 n = strlen(s+1), m = read(), pwr[0] = 1; 101 for (int i=1; i<=n; i++) 102 { 103 pwr[i] = pwr[i-1]*base; 104 for (int j=1; j<=26; j++) hsh[i][j] = hsh[i-1][j]*base+(j==s[i]-'a'+1); 105 } 106 build(1, 1, n); 107 for (int i=1; i<=m; i++) 108 { 109 int opt = read(); 110 if (opt==1){ 111 int x = read(), y = read(), k = read(); 112 for (int i=1; i<=26; i++) retx[i] = rety[i] = 0; 113 query(1, 1, n, x, y, retx); 114 query(1, 1, n, k, k+y-x, rety); 115 bool legal = true; 116 for (int i=1; i<=26&&legal; i++) 117 if (retx[i]!=rety[i]) legal = false; 118 puts(legal?"Y":"N"); 119 }else if (opt==2){ 120 int x = read(), y = read(), k = read(); 121 addtag(1, 1, n, x, y, k); 122 }else{ 123 int x = read(), y = read(); 124 circulation(1, 1, n, x, y); 125 } 126 } 127 return 0; 128 }
END