CF 1320D Reachable Strings 后缀数组
确定性解法:后缀数组
众所周知 hash 由于不能完全避免冲突常常会被 cf hacker 卡掉,此题有更稳定的解法
考虑将原串进行简化,由于只有连续的 1 会造成不确定的匹配,可以只对 0 建立一个新串
对于一段连续的 1,后面的 0 建立新字符,如果有奇数个 1 就建立 1,偶数个就建立 0
例如: 1110 -> 1,110 -> 0,11011 -> 0
由于最后的 1 是没有后导 0 的就不计入新串
然后新串的 lcp 就可以对应到原串的对应位置,新串 lcp 匹配就等价于原串可达
比如 011010 和 110010 转换后都会变为 001
然后考虑左右两端不是 0 的情况
就类似分块,将左右边界用 1 的数量特判即可,特判的情况很多注意细节
- 区间全是 1
- 区间只有一个 0 (边界特判完后没有 0)
#include <bits/stdc++.h>
#define ll long long
#define X first
#define Y second
#define sz size()
#define all(x) x.begin(), x.end()
using namespace std;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<long long> vl;
template<class T>
inline bool scan(T &ret) {
char c;
int sgn;
if (c = getchar(), c == EOF) return 0; //EOF
while (c != '-' && (c < '0' || c > '9')) c = getchar();
sgn = (c == '-') ? -1 : 1;
ret = (c == '-') ? 0 : (c - '0');
while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
ret *= sgn;
return 1;
}
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 50;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
ll qp(ll x, ll n) {
ll res = 1;
x %= mod;
while (n > 0) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
char t[maxn], s[maxn];
namespace SA {
const int maxn = 2e5 + 10;
int t1[maxn], t2[maxn], c[maxn];
int Rank[maxn], height[maxn];
int RMQ[maxn];
int mm[maxn];
int sa[maxn];
int best[25][maxn];
bool cmp(int *r, int a, int b, int l) {
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(char str[], int sa[], int Rank[], int height[], int n, int m) {
n++;
int i, j, p, *x = t1, *y = t2;
for (i = 0; i < m; i++)c[i] = 0;
for (i = 0; i < n; i++)c[x[i] = str[i]]++;
for (i = 1; i < m; i++)c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
for (j = 1; j <= n; j <<= 1) {
p = 0;
for (i = n - j; i < n; i++)y[p++] = i;
for (i = 0; i < n; i++)if (sa[i] >= j)y[p++] = sa[i] - j;
for (i = 0; i < m; i++)c[i] = 0;
for (i = 0; i < n; i++)c[x[y[i]]]++;
for (i = 1; i < m; i++)c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
if (p >= n)break;
m = p;
}
int k = 0;
n--;
for (i = 0; i <= n; i++)Rank[sa[i]] = i;
for (i = 0; i < n; i++) {
if (k)k--;
j = sa[Rank[i] - 1];
while (str[i + k] == str[j + k])k++;
height[Rank[i]] = k;
}
}
void initRMQ(int n) {
for (int i = 1; i <= n; i++)
RMQ[i] = height[i];
mm[0] = -1;
for (int i = 1; i <= n; i++)
mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
for (int i = 1; i <= n; i++)best[0][i] = i;
for (int i = 1; i <= mm[n]; i++)
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
int a = best[i - 1][j];
int b = best[i - 1][j + (1 << (i - 1))];
if (RMQ[a] < RMQ[b])best[i][j] = a;
else best[i][j] = b;
}
}
int askRMQ(int a, int b) {
int t;
t = mm[b - a + 1];
b -= (1 << t) - 1;
a = best[t][a];
b = best[t][b];
return RMQ[a] < RMQ[b] ? a : b;
}
int lcp(int a, int b) {
a = Rank[a];
b = Rank[b];
if (a > b) swap(a, b);
//cout << askRMQ(a + 1, b) << endl;
return height[askRMQ(a + 1, b)];
}
void preprocess(char *str, int n, int m) {
da(str, sa, Rank, height, n, m);
initRMQ(n);
}
}
int n, q;
int sum[maxn], L[maxn], R[maxn], id[maxn];
int main(int argc, char *argv[]) {
scanf("%d%s%d", &n, t + 1, &q);
int pre = -1;
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + (t[i] == '0');
if (t[i] == '0') pre = i;
L[i] = pre;
}
pre = -1;
for (int i = n; i >= 1; --i) {
if (t[i] == '0') pre = i;
R[i] = pre;
}
int tot = 0;
pre = 0;
for (int i = 1; i <= n; ++i) {
if (t[i] == '0') {
s[id[i] = tot++] = (pre & 1) + '0';
pre = 0;
} else pre++;
}
SA::preprocess(s, tot, 130);
for (int i = 1; i <= q; ++i) {
int l1, l2, len;
scanf("%d%d%d", &l1, &l2, &len);
int r1 = l1 + len - 1;
int r2 = l2 + len - 1;
/*putchar(' ');
for (int j = l1; j <= r1; ++j) putchar(s[j]);
putchar(' ');
for (int j = l2; j <= r2; ++j) putchar(s[j]);
putchar(' ');*/
int z1 = sum[r1] - sum[l1 - 1];
int z2 = sum[r2] - sum[l2 - 1];
if (z1 != z2) {
puts("NO");
continue;
}
if (z1 == 0) {
puts("YES");
continue;
}
int front1 = R[l1] - l1, front2 = R[l2] - l2, last1 = r1 - L[r1], last2 = r2 - L[r2];
if (front1 % 2 != front2 % 2 || last1 % 2 != last2 % 2) {
puts("NO");
continue;
}
if (z1 == 1) {
puts("YES");
continue;
}
if (id[R[l1]] == id[R[l2]]) {
puts("YES");
continue;
}
int alen = SA::lcp(id[R[l1]] + 1, id[R[l2]] + 1);
if (alen >= z1 - 1) {
puts("YES");
} else puts("NO");
}
return 0;
}