cp-library

競技プログラミング用 C++ ライブラリ

View the Project on GitHub qandori/cp-library

:warning: 木のハッシュ

tree/tree_hash.hpp · GitHub で見る

共通ページ

根を持たない無向木全体の形のハッシュを求めます。頂点番号と隣接リストの順序に依存せず、木同士の同型判定に使えます。

#include "lib/tree/tree_hash.hpp"

std::vector<std::vector<int>> first{ {1}, {0, 2, 3}, {1}, {1} };
std::vector<std::vector<int>> second{ {3}, {3}, {3}, {0, 1, 2} };
auto a = tree_hash(first);
auto b = tree_hash(second);
bool same = a == b; // true: 頂点番号だけ異なる星

tree_hash(graph,seed = 既定 seed) は std::array<TreeHashValue,2> を返します。木の重心が 2 個あれば、それぞれを根にした木全体のハッシュを昇順に並べます。重心が 1 個なら同じハッシュを 2 個入れます。戻り値全体を == で比較してください。大小比較・ソートや std::map のキーとしても使えます。

入力は std::vector<std::vector<int>> の無向木です。頂点数は INT_MAX 以下とし、各辺を両方向に 1 回ずつ入れ、連結・自己ループなし・多重辺なしとしてください。空の木にも対応し、2 個のゼロ値を返します。入力を変更せず、返した値は入力の寿命に依存しません。

TreeHashValue は 2 本の 61 bit ハッシュを持ちます。既定 seed は 根付き木のハッシュ と共通で、同じプロセス内では別々に構築した木同士を比較できます。値を保存して実行をまたいで比較する場合は、tree_hash(graph,12345) のように同じ seed を指定してください。

木の同型写像は重心同士を対応させるため、同型な木の戻り値は必ず一致します。異なる木がハッシュ衝突する可能性はあります。辺・頂点のラベルや重みは扱いません。根を指定して全ての部分木のハッシュを求める場合は 根付き木のハッシュ を使ってください。

子の順序のソートや再帰は使いません。

頂点数を \(n\) として、計算は \(O(n)\) 時間・追加空間です。返すハッシュは \(O(1)\) 空間で、比較は \(O(1)\) 時間です。

依存ライブラリ

コード

#ifndef TREE_HASH_HPP
#define TREE_HASH_HPP

#include "../components/tree_hash_core.hpp"
#include <utility>

inline std::array<TreeHashValue, 2> tree_hash(const std::vector<std::vector<int>> &graph,
	std::uint64_t seed = tree_hash_detail::default_seed()) {
	std::array<TreeHashValue, 2> result{};
	if (graph.empty()) return result;
	auto tree = tree_hash_detail::root_tree(graph, 0);
	const int n = static_cast<int>(graph.size());
	int first = 0;
	for (int v = 1; v < n; ++v)
		if (tree.size[v] >= n / 2 + n % 2 && tree.size[v] < tree.size[first]) first = v;
	int second = tree.size[first] == n / 2 ? tree.parent[first] : -1;
	if (first != 0) tree = tree_hash_detail::root_tree(graph, first);
	auto hashes = tree_hash_detail::hashes(graph, tree, seed);
	result[0] = hashes[first];
	if (second == -1) result[1] = result[0];
	else {
		int height = 0;
		for (int u : graph[first]) if (u != second) height = std::max(height, tree.height[u] + 1);
		auto salt = tree_hash_detail::salt_at(height, seed);
		TreeHashValue branch{ {1, 1} };
		for (int u : graph[first]) if (u != second) tree_hash_detail::append(branch, hashes[u], salt);
		salt = tree_hash_detail::salt_at(std::max(tree.height[second], height + 1), seed);
		result[1] = { {1, 1} };
		for (int u : graph[second]) tree_hash_detail::append(result[1], u == first ? branch : hashes[u], salt);
		if (result[1] < result[0]) std::swap(result[0], result[1]);
	}
	return result;
}

#endif
Back to top page