树同构/树哈希

题目

P5043 【模板】树同构([BJOI2015]树的同构)

树的同构与哈希

判断两棵树是否同构,可以用树哈希实现,两颗树的哈希值一样时,我们可以判定他们同构。树的哈希只在有根树中有意义。当要判定两棵树是否同构时,我们可以选择它们的重心为根进行哈希。时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

关于树哈希,用的比较多的方法是将子树从小到大排序,对于第 i i i个子树,乘上第 i i i个质数

v a l [ y ] = ∑ x ∈ s o n y v a l [ x ] ∗ p r i m e [ s i z e ( x ) ] val[y]=\sum_{x\in {son_y}} val[x]*prime[size(x)] val[y]=xsonyval[x]prime[size(x)]

当然,也有很多哈希方法值得尝试,比如字符串哈希中使用较多的进制哈希写法简单,也很不错。

v a l [ y ] = ∑ x ∈ s o n y v a l [ x ] ∗ b a s e c n t [ x ] val[y]=\sum_{x\in {son_y}} val[x]*base^{cnt[x]} val[y]=xsonyval[x]basecnt[x]

个人比较喜欢的哈希是异或 ,一般瞎hash一下就不会被卡。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 50 + 5;
vector<int> mp[N];
vector<pair<int, int>> centr[N];
int siz[N], mx[N]; 
int n;
int dfs(int u, int fa)
{
    int res = 0;
    siz[u] = 0;
    for (auto v : mp[u])
    {
        if (v == fa)
            continue;
        int val = dfs(v, u);
        siz[u] += val;
        res = max(res, val);
    }
    mx[u] = max(res, n - (siz[u] + 1));
    return siz[u] + 1;
}
void init()
{
    for (int i = 0; i < N; i++)
        mp[i].clear(), mx[i] = 0;
}
int Hash(int u, int fa)
{
    int sum = 1;
    vector<int> res;
    for (auto v : mp[u])
    {
        if (v == fa)
            continue;
        res.emplace_back(Hash(v, u));
    }
    sort(res.begin(), res.end());//必须要排序才能保证正确性!
    for (auto it : res)
        sum += it * 131 ^ 133331;
    return sum;
}
int get(int x)
{
    init();
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        int fa;
        cin >> fa;
        if (fa == 0)
            continue;
        mp[i].emplace_back(fa);
        mp[fa].emplace_back(i);
    }
    dfs(1, -1);
    int maxx = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++)
        maxx = min(maxx, mx[i]);
    for (int i = 1; i <= n; i++)
        if (maxx == mx[i])
            centr[x].emplace_back(make_pair(i, Hash(i, -1)));
    for (auto it : centr[x])
        for (int j = 1; j <= x; j++)
            for (auto it2 : centr[j])
                if (it.second == it2.second)
                    return j;
    return -1;
}
int main()
{
    int m;
    cin >> m;
    for (int i = 1; i <= m; i++)
        cout << get(i) << endl;
    return 0;
}

### 哈希算法 哈希算法一种将任意长度的数据映射为固定长度输出的算法。输出通常称为哈希值或哈希码。哈希算法具有以下特点: - **确定性**:相同的输入总是产生相同的输出。 - **不可逆性**:无法从哈希值反推出原始输入。 - **抗冲突性**:尽量减少不同输入产生相同哈希值的可能性。 哈希算法广泛应用于数据完整性校验、密码存储、数据索引等领域。例如,MD5、SHA-1、SHA-256等是常见的哈希算法。 ### 异或哈希 异或哈希是一种简单的哈希技术,通常用于快速计算数据的哈希值。它通过将数据的各个部分进行异或操作来生成哈希值。异或操作具有以下特性: - **可交换性**:异或操作的顺序不影响结果。 - **可逆性**:异或操作可以通过再次异或相同值来还原原始值。 异或哈希的优点是计算速度快,但其缺点是容易发生哈希冲突,因为不同的数据组合可能产生相同的哈希值。因此,异或哈希通常用于对性能要求较高但对安全性要求较低的场景,例如快速校验数据完整性或简单的数据索引。 ### 树哈希 树哈希一种专门用于树形数据结构的哈希算法。它通过对树中的每个节点进行哈希计算,并将这些哈希值组合起来,以生成整个树的唯一哈希值。树哈希可以用于快速比较两棵树是否相同或检测树的修改。 树哈希的计算方式通常包括以下几种: - **叶节点哈希**:首先对叶节点进行哈希计算,然后逐步向上合并父节点的哈希值。 - **重心优化**:通过找树的重心来优化计算复杂度,减少不必要的计算[^3]。 - **子树哈希组合**:通过将子节点的哈希值与某种权重结合,计算父节点的哈希值[^4]。 树哈希的一个典型应用场景是树的同构检测,例如在分布式系统中比较树的结构是否一致,或者在版本控制系统中检测树的结构变化。 ### 区别与应用场景 | 特性/算法 | 哈希算法 | 异或哈希 | 树哈希 | |----------------|------------------------|------------------------|------------------------------| | **特点** | 确定性、不可逆性、抗冲突性 | 可交换性、可逆性 | 适用于树形结构 | | **计算复杂度** | 通常较高 | 非常低 | 中等 | | **安全性** | 高 | 低 | 依赖具体实现 | | **应用场景** | 数据完整性校验、密码存储 | 快速校验、简单索引 | 树的同构检测、结构比较 | ### 示例代码:树哈希实现 以下是一个简单的树哈希实现,假设每棵树的节点值为整数,且哈希值通过子节点的哈希值加权求和得到: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MOD = 1000000007; const int BASE = 7397; struct TreeNode { int value; vector<TreeNode*> children; }; int computeHash(TreeNode* node) { if (node == nullptr) return 131; // Base case for null node vector<int> childHashes; for (TreeNode* child : node->children) { childHashes.push_back(computeHash(child)); } sort(childHashes.begin(), childHashes.end()); int hash = 1; for (int h : childHashes) { hash = (1LL * hash * BASE + h) % MOD; } return (hash + node->value) % MOD; } int main() { // 构建树并计算哈希值 TreeNode root; root.value = 1; TreeNode child1; child1.value = 2; root.children.push_back(&child1); TreeNode child2; child2.value = 3; root.children.push_back(&child2); int treeHash = computeHash(&root); cout << "Tree hash: " << treeHash << endl; return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值