Description
给定一个 01 01 01 串。每次询问两个等长的子串,求是否可以从一个经过数次变换变成另一个,变换操作的定义是每次选定一个包含 110 110 110 或 011 011 011 的子串,将其中的 110 110 110 换成 011 011 011 或将其中的 011 011 011 换成 110 110 110。
1 ≤ n , q ≤ 2 × 1 0 5 1 \leq n,q \leq 2 \times 10^5 1≤n,q≤2×105。
Solution
011 011 011 和 110 110 110 的变换本质上是将 0 0 0 移动 2 2 2 位。具体的,一个 0 可以向左或向右移动 2 2 2 位,前提是移动的路径上没有 0 0 0。
如果将串分成若干段,每一段只有一个 0 0 0。如果 0 0 0 在偶数位,那么这个 0 0 0 可以移动到这个段上偶数位,如果 0 0 0 在奇数位,那么这个 0 0 0 可以移动到这个奇数位。这意味着如果两个字符串 0 0 0 的出现位置的奇偶性对应,那么它们可以通过变换相同,奇偶性是指的选定子串的奇偶性。
所以可以 01 01 01 串进行奇偶性 hash。要写两个 hash,分别表示当前位在询问子串中是奇数位或偶数位的 hash。同时预处理一个 Base 的幂次,每个前缀中 0 0 0 的个数即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, INF = 0x3f3f3f3f, p = 1e9 + 7, Base = 233;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
char s[N];
int power[N], cnt[N], hs[N][2];
int get(int l, int r) {
return (hs[r][l & 1] - hs[l - 1][l & 1] * power[cnt[r] - cnt[l - 1]] % p + p) % p;
}
signed main() {
int n = read(); scanf("%s", s + 1);
int Q = read();
power[0] = 1;
for (int i = 1; i <= n; i++) {
power[i] = power[i - 1] * Base % p, cnt[i] = cnt[i - 1];
hs[i][0] = hs[i - 1][0], hs[i][1] = hs[i - 1][1];
if (s[i] == '0') {
cnt[i]++;
hs[i][0] = (hs[i - 1][0] * Base + (i & 1) + 1) % p, hs[i][1] = (hs[i - 1][1] * Base + (i & 1 ^ 1) + 1) % p;
}
}
while (Q--) {
int l1 = read(), l2 = read(), len = read();
if (get(l1, l1 + len - 1) == get(l2, l2 + len - 1)) puts("Yes");
else puts("No");
}
return 0;
}