[NOI2018]你的名字

题目链接
先考虑68分的做法:
求在A串中出现,且在B串中没出现的串的数量。
使用容斥,用A的不同子串数减去A,B的不同公共子串数。
先用双指针,求出A的每个位置开始,在B中最多能向后匹配多远。
然后,问题变为,给你一些区间,问它们的子区间中有多少不同的串。
因为每个串,都是原区间([l,r])([l,i])的后缀。((l<=i<=r))
而后缀就是在前面去掉一些字符,就是不断走fa。
所以可以建出fa边的树,然后定位出这些位置,求这些点到根的路径的并。
使用树链合并即可,要注意压缩的问题。
100分做法:与68基本相同,就是判断子串([a,b])是否出现多了一个限制((l<=a,b<=r))。
而一个串匹配的位置,就是这个点子树中红点的集合。
红点对应前缀,所以这个限制可以转化为前缀位置的限制。
需要判断一个点的子树中是否有x~y的数。使用DFS序+主席树即可。

代码:

#include <stdio.h> 
#include <stdlib.h> 
#define ll long long 
int fr[2000010],ne[2000010];
int v[2000010],bs = 0;
int wl[2000010],wr[2000010],tm = 1;
int xl[2000010];
struct SAM {
	int trs[2000010][26],fa[2000010];
	int len[2000010],np,sl;
	int red[2000010];
	SAM() {
		np = sl = 1;
	}
	void clean() {
		for (int i = 1; i <= sl; i++) {
			fa[i] = len[i] = 0;
			red[i] = false;
			for (int j = 0; j < 26; j++) trs[i][j] = 0;
		}
		np = sl = 1;
	}
	void insert(char c, int wz) {
		c -= 'a';
		int p = np;
		np = ++sl;
		len[np] = len[p] + 1;
		while (p != 0 && trs[p][c] == 0) {
			trs[p][c] = np;
			p = fa[p];
		}
		if (p == 0) fa[np] = 1;
		else {
			int q = trs[p][c];
			if (len[q] == len[p] + 1) fa[np] = q;
			else {
				int nq = ++sl;
				fa[nq] = fa[q];
				fa[q] = fa[np] = nq;
				len[nq] = len[p] + 1;
				for (int j = 0; j < 26; j++) trs[nq][j] = trs[q][j];
				while (p != 0 && trs[p][c] == q) {
					trs[p][c] = nq;
					p = fa[p];
				}
			}
		}
		red[np] = wz;
	}
};
void addb(int a, int b) {
	v[bs] = b;
	ne[bs] = fr[a];
	fr[a] = bs;
	bs += 1;
}
SAM S,T;
void dfs1(int u) {
	xl[tm] = u;
	wl[u] = tm++;
	for (int i = fr[u]; i != -1; i = ne[i]) dfs1(v[i]);
	wr[u] = tm;
}
int up[1000010];
char zf[1000010];
struct SPx {
	int u,cd;
	SPx() {}
	SPx(int U, int Cd) {
		u = U;
		cd = Cd;
	}
};
SPx px[1000010];
ll baoli(int s) {
	for (int i = 1; i <= T.sl; i++) up[i] = 0;
	for (int i = 0; i < s; i++) {
		int cd = px[i].cd,
		u = px[i].u;
		while (u != 1) {
			if (up[u] == T.len[u] - T.len[T.fa[u]]) break;
			if (cd - T.len[T.fa[u]] > up[u]) up[u] = cd - T.len[T.fa[u]];
			u = T.fa[u];
			cd = T.len[u];
		}
	}
	ll rtn = 0;
	for (int i = 2; i <= T.sl; i++) rtn += up[i];
	return rtn;
}
int he[24000010],cl[24000010],cr[24000010],sl = 0;
int jianshu(int l, int r) {
	int rt = sl++;
	he[rt] = 0;
	if (l + 1 == r) return rt;
	int m = (l + r) >> 1;
	cl[rt] = jianshu(l, m);
	cr[rt] = jianshu(m, r);
	return rt;
}
int xiugai(int i, int l, int r, int j, int x) {
	int rt = sl++;
	if (l + 1 == r) {
		he[rt] = he[i] + x;
		return rt;
	}
	cl[rt] = cl[i];
	cr[rt] = cr[i];
	int m = (l + r) >> 1;
	if (j < m) cl[rt] = xiugai(cl[rt], l, m, j, x);
	else cr[rt] = xiugai(cr[rt], m, r, j, x);
	he[rt] = he[cl[rt]] + he[cr[rt]];
	return rt;
}
int chaxun(int i, int l, int r, int L, int R) {
	if (R <= l || r <= L) return 0;
	if (L <= l && r <= R) return he[i];
	int m = (l + r) >> 1;
	return chaxun(cl[i], l, m, L, R) + chaxun(cr[i], m, r, L, R);
}
int gen[1000010],n;
bool zichuan(int i, int l, int r, int cd) {
	l = l + cd - 1;
	if (l > r) return false;
	int z = chaxun(gen[wr[i] - 1], 1, n + 1, l, r + 1) - chaxun(gen[wl[i] - 1], 1, n + 1, l, r + 1);
	return z > 0;
}
int main() {
	scanf("%s", zf);
	for (n = 0; zf[n] != 0; n++) S.insert(zf[n], n + 1);
	for (int i = 1; i <= S.sl; i++) fr[i] = -1;
	for (int i = 1; i <= S.sl; i++) addb(S.fa[i], i);
	dfs1(1);
	gen[0] = jianshu(1, n + 1);
	for (int i = 1; i < tm; i++) {
		if (S.red[xl[i]] != 0) gen[i] = xiugai(gen[i - 1], 1, n + 1, S.red[xl[i]], 1);
		else gen[i] = gen[i - 1];
	}
	int m;
	scanf("%d", &m);
	for (int i = 0; i < m; i++) {
		int l,r,x = 1,y = 1,s = 0;
		scanf("%s%d%d", zf, &l, &r);
		T.clean();
		for (int j = 0; zf[j] != 0; j++) T.insert(zf[j], 0);
		for (int j = 0, k = -1, c = 0; zf[j] != 0; j++) {
			c -= 1;
			if (x != 1 && k - j == S.len[S.fa[x]]) x = S.fa[x];
			if (y != 1 && k - j == T.len[T.fa[y]]) y = T.fa[y];
			if (k < j) k = j,
			c = 0;
			while (zf[k] != 0 && S.trs[x][zf[k] - 'a'] != 0 && zichuan(S.trs[x][zf[k] - 'a'], l, r, c + 1)) {
				c += 1;
				x = S.trs[x][zf[k] - 'a'];
				y = T.trs[y][zf[k] - 'a'];
				px[s++] = SPx(y, c);
				k += 1;
			}
		}
		ll zo = 0;
		for (int j = 1; j <= T.sl; j++) zo = zo + T.len[j] - T.len[T.fa[j]];
		printf("%lld
", zo - baoli(s));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/lnzwz/p/11354710.html