Fail 树(失配树)

5 篇文章 0 订阅
2 篇文章 0 订阅

Fail 树

我们在写 K M P KMP KMP 时会求出来长度为 N o d e Node Node 的前缀最长 b o r d e r border border 的长度 N e x t [ N o d e ] Next[Node] Next[Node]
根据前面的理论,对于一个字符串 S S S,它的 b o r d e r border border 集合等于它的最长 b o r d e r border border 的 ( b o r d e r border border 集合 + 本身)。
对于长度为 N N N 的字符串,我们建立 N + 1 N + 1 N+1 个点,编号为 0 0 0 N N N。对于点 N o d e Node Node,我们连一条单向边 ( N e x t [ N o d e ] , N o d e ) (Next[Node], Node) (Next[Node],Node),则会形成一颗有根树,树根为 0 0 0,这棵树就叫做 f a i l fail fail 树。

Fail 树的性质

  • 对于一个点 N o d e Node Node,它的 b o r d e r border border 集合便是它到树根的路径上所有点对应的前缀。
  • N o d e 1 Node1 Node1 和点 N o d e 2 Node2 Node2 f a i l fail fail 树的 LCA 指的是长度分别是 N o d e 1 Node1 Node1 N o d e 2 Node2 Node2 的前缀的最长公共 B o r d e r Border Border

解决模板题 P5829

LCA

方法

使用倍增。
F a [ N o d e ] [ i ] Fa[Node][i] Fa[Node][i] 表示点 N o d e Node Node 2 i 2^i 2i 级祖先, F a [ N o d e ] [ 0 ] Fa[Node][0] Fa[Node][0] 就是它的父节点。
那么对于 i ⩾ 1 i \geqslant 1 i1,我们有转移式: F a [ N o d e ] [ i ] = F a [ F a [ N o d e ] [ i − 1 ] ] [ i − 1 ] Fa[Node][i]=Fa[Fa[Node][i-1]][i-1] Fa[Node][i]=Fa[Fa[Node][i1]][i1]
也就是说,点 N o d e Node Node 2 i 2^i 2i 级祖先就是点 N o d e Node Node 2 i − 1 2^{i-1} 2i1 级祖先 的 2 i − 1 2^{i-1} 2i1 级祖先。( 2 i − 1 + 2 i − 1 = 2 i − 1 ∗ 2 = 2 i 2^{i-1}+2^{i-1}=2^{i-1}*2=2^i 2i1+2i1=2i12=2i
对于树上的每个点我们都能处理 F a Fa Fa 的值。
N o d e 1 Node1 Node1 N o d e 2 Node2 Node2 的 LCA 时,假设深度 D e p [ N o d e 1 ] > D e p [ N o d e 2 ] Dep[Node1] > Dep[Node2] Dep[Node1]>Dep[Node2],我们先计算出深度差 D i f = D e p [ N o d e 1 ] − D e p [ N o d e 2 ] Dif = Dep[Node1] - Dep[Node2] Dif=Dep[Node1]Dep[Node2],然后把 D e p Dep Dep 转成二进制,通过倍增跳跃使得点 N o d e 1 Node1 Node1 和点 N o d e 2 Node2 Node2 深度相同。
然后,若点 N o d e 1 Node1 Node1 和点 N o d e 2 Node2 Node2 相同,则 N o d e 1 Node1 Node1 (或 N o d e 2 Node2 Node2) 就是 LCA。
否则,我们先考虑 2 20 2^{20} 220 2 2 2 的某个次幂,只要满足 > N > N >N 就行),如果 N o d e 1 Node1 Node1 N o d e 2 Node2 Node2 同时跳 2 20 2^{20} 220 级祖先仍然不同,那么就跳,否则不动;接着枚举 2 19 2^{19} 219,以此类推。(注意不能越界)
到最后, N o d e 1 Node1 Node1 N o d e 2 Node2 Node2 只需要再跳一步就是 LCA 了。时间复杂度是 Θ ( log ⁡ N ) \Theta(\log N) Θ(logN) 的。

代码(LCA)
void DFS(int Current, int Previous) {
	//	std::cout << "DFS\n";
	Tree[Current][0] = Previous;
	for (int i = 1; i <= Log2[Dep[Current]]; i++) {
		Tree[Current][i] = Tree[Tree[Current][i - 1]][i - 1];
	}
	for (int Node2 : Gr[Current]) {
		Dep[Node2] = Dep[Current] + 1;
		DFS(Node2, Current);
	}
}
int LCA(int A, int B) {
	//std::cout << "LCA\n";
	if (Dep[A] < Dep[B]) std::swap(A, B);
	while (Dep[A] > Dep[B]) A = Tree[A][Log2[Dep[A] - Dep[B]]];
	if (A == B) return A;
	for (int i = Log2[Dep[A]]; i >= 0; i--) {
		if(Tree[A][i] != Tree[B][i]) {
			A = Tree[A][i];
			B = Tree[B][i];
		}
	}
	return Tree[A][0];
}

代码 (P5829)

std::vector<int> Gr[1000005];
std::vector<int> Next;
std::vector<int> GetNext(std::string Str) {
	//std::cout << "Next\n";
	static std::vector<int> Result(Str.size() + 3, 0);
	int Len = Str.size() - 1;
	int j = 0;
	for (int i = 2; i <= Len; i++) {
		while (j && Str[j + 1] != Str[i]) j = Result[j];
		if (Str[j + 1] == Str[i]) j++;
		Result[i] = j;
	}
	return Result;
}
int Tree[1000005][22];
int Dep[1000005], Log2[1000005];
void DFS(int Current, int Previous) {
//	std::cout << "DFS\n";
	Tree[Current][0] = Previous;
	for (int i = 1; i <= Log2[Dep[Current]]; i++) {
		Tree[Current][i] = Tree[Tree[Current][i - 1]][i - 1];
	}
	for (int Node2 : Gr[Current]) {
		Dep[Node2] = Dep[Current] + 1;
		DFS(Node2, Current);
	}
}
int LCA(int A, int B) {
	//std::cout << "LCA\n";
	if (Dep[A] < Dep[B]) std::swap(A, B);
	while (Dep[A] > Dep[B]) A = Tree[A][Log2[Dep[A] - DepB]]];
	if (A == B) return A;
	for (int i = Log2[Dep[A]]; i >= 0; i--) {
		if(Tree[A][i] != Tree[B][i]) {
			A = Tree[A][i];
			B = Tree[B][i];
		}
	}
	return Tree[A][0];
}
std::vector<int> P5829(std::string Str, int M, std::vector<int> P, std::vector<int> Q) {
	//std::cout << "P5829\n";
	Dep[0] = 0;
	Str = '#' + Str;
	Log2[1] = 0;
	int Len = Str.size() - 1;
	for (int i = 2; i < 1000005; i++) {
		Log2[i] = Log2[i - 1] + ((i & -i) == i);
	}
	Next = GetNext(Str);
	for (int i = 1; i <= Len; i++) Gr[Next[i]].push_back(i);
	DFS(0, 0);
	std::vector<int> Result(0);
	for (int i = 0; i < M; i++) {
		Result.push_back(LCA(Tree[P[i]][0], Tree[Q[i]][0]));
	}
	return Result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值