BZOJ 4556: [Tjoi2016&Heoi2016]字符串

两个状态的最长公共后缀是他们在parent树上的LCA
那么将串反过来就是LCP了
二分答案,找到d所在状态的parent树上最高的节点,且那个节点的最大长度大于等于二分的值
这个倍增即可
这个状态的right集合最大,再查看right集合中是否存在 a, b 之间的某个位置即可
用线段树合并维护right集合

#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define pii pair<int, int>
#define pli pair<ll, int>
#define SZ(x) ((int)(x).size())
#define lp tree[p].l
#define rp tree[p].r
#define mid ((l + r) / 2)
#define lowbit(i) ((i) & (-i))
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=((b)-1);i>=(a);i--)
#define Edg int ccnt=1,head[N],to[E],ne[E];void addd(int u,int v){to[++ccnt]=v;ne[ccnt]=head[u];head[u]=ccnt;}void add(int u,int v){addd(u,v);addd(v,u);}
#define Edgc int ccnt=1,head[N],to[E],ne[E],c[E];void addd(int u,int v,int w){to[++ccnt]=v;ne[ccnt]=head[u];c[ccnt]=w;head[u]=ccnt;}void add(int u,int v,int w){addd(u,v,w);addd(v,u,w);}
#define es(u,i,v) for(int i=head[u],v=to[i];i;i=ne[i],v=to[i])
const int MOD = 1000000007, INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
void M(int &x) {if (x >= MOD)x -= MOD; if (x < 0)x += MOD;}
int qp(int a, int b = MOD - 2, int mod = MOD) {int ans = 1; for (; b; a = 1LL * a * a % mod, b >>= 1)if (b & 1)ans = 1LL * ans * a % mod; return ans % mod;}
template<class T>T gcd(T a, T b) { while (b) { a %= b; std::swap(a, b); } return a; }
template<class T>bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<class T>bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int N = 2e5 + 7;
struct Tree {
	int l, r, sum;
} tree[N * 30];
int ch[N][26], len[N], fail[N], id[N], fa[N][22];
int last, tol, cnt, root[N];
int n, m;
void modify(int &p, int l, int r, int pos) {
	p = ++cnt;
	tree[p].sum++;
	if (l == r) return;
	if (pos <= mid) modify(lp, l, mid, pos);
	else modify(rp, mid + 1, r, pos);
}
int merge(int x, int y, int l, int r) {
	if (!x || !y) return x | y;
	int p = ++cnt;
	tree[p].sum = tree[x].sum + tree[y].sum;
	if (l < r) {
		tree[p].l = merge(tree[x].l, tree[y].l, l, mid);
		tree[p].r = merge(tree[x].r, tree[y].r, mid + 1, r);
	} else {
		tree[p].l = tree[p].r = 0;
	}
	return p;
}
int query(int p, int l, int r, int x, int y) {
	if (!p || l > r || x > y || !tree[p].sum) return 0;
	if (x <= l && y >= r) return tree[p].sum;
	int res = 0;
	if (x <= mid) res += query(lp, l, mid, x, y);
	if (y > mid) res += query(rp, mid + 1, r, x, y);
	return res;
}
void init() {
	len[tol = last = 0] = 0; fail[0] = -1;
	rep (i, 0, 26) ch[0][i] = 0;
	tol++;
}
int newnode() {
	int p = tol++;
	rep (i, 0, 26) ch[p][i] = 0;
	len[p] = fail[p] = 0;
	return p;
}
void extend(int c) {
	int cur = newnode();
	len[cur] = len[last] + 1;
	id[len[cur]] = cur;
	modify(root[cur], 1, n, len[cur]);
	int p = last;
	while (p != -1 && !ch[p][c]) {
		ch[p][c] = cur;
		p = fail[p];
	}
	if (p == -1) {
		fail[cur] = 0;
	} else {
		int q = ch[p][c];
		if (len[p] + 1 == len[q]) {
			fail[cur] = q;
		} else {
			int clone = newnode();
			len[clone] = len[p] + 1;
			memcpy(ch[clone], ch[q], sizeof(ch[q]));
			fail[clone] = fail[q];
			while (p != -1 && ch[p][c] == q) {
				ch[p][c] = clone;
				p = fail[p];
			}
			fail[q] = fail[cur] = clone;
		}
	}
	last = cur;
}
bool check(int le, int p, int l, int r) {
	per (i, 0, 20) if (len[fa[p][i]] >= le) p = fa[p][i];
	return query(root[p], 1, n, l + le - 1, r);
}

char s[N];
int a[N], c[N];

int main() {
#ifdef LOCAL
	freopen("ans.out", "w", stdout);
#endif
	scanf("%d%d", &n, &m);
	scanf("%s", s + 1);
	std::reverse(s + 1, s + 1 + n);
	init();
	rep (i, 1, n + 1) extend(s[i] - 'a');
	rep (i, 0, tol) c[len[i]]++;
	rep (i, 1, tol) c[i] += c[i - 1];
	rep (i, 0, tol) a[--c[len[i]]] = i;
	per (i, 0, tol) {
		int p = a[i];
		if (!p) continue;
		if (fail[p] >= 0) fa[p][0] = fail[p], root[fail[p]] = merge(root[fail[p]], root[p], 1, n);
	}
	rep (j, 1, 20) rep (i, 0, tol) fa[i][j] = fa[fa[i][j - 1]][j - 1];
	rep (i, 0, m) {
		int a, b, c, d;
		scanf("%d%d%d%d", &a, &b, &c, &d);
		a = n - a + 1, b = n - b + 1, c = n - c + 1, d = n - d + 1;
		std::swap(a, b); std::swap(c, d);
		assert(a <= b && c <= d);
		int l = 1, r = std::min(b - a + 1, d - c + 1), ans = 0;
		while (l <= r) {
			if (check(mid, id[d], a, b)) l = (ans = mid) + 1;
			else r = mid - 1;
		}
		printf("%d\n", ans);
	}
#ifdef LOCAL
	printf("%.10f\n", (db)clock() / CLOCKS_PER_SEC);
#endif
	return 0;
}
posted @ 2020-03-18 17:36  Mrzdtz220  阅读(184)  评论(0编辑  收藏  举报