树的同构 ( 附带 POJ 1635 )

前言

POJ 1635 这是刷字符串的时候被煞笔天海放进来的一道题,题意是让你判断两棵树是否同构。
我想他的主要目的应该是听了肥宅带鱼的B话,把树上的最小表示法也放进来了……
这特么完全不是一个算法好么 虽然可以转化

必要知识 (可跳过

首先我们必须得知道两棵树的同构是如何定义的。
一棵树通过不断交换他的子树位置,最后能够与另一棵树完全相同,则称这两颗树同构。

( 例子我就不举了……

树上的最小表示法

最先看得就是树上的最小表示法,其实树上的最小表示法,并没有什么难的,反而可以说是以中非常暴力的方法。判断同构,非常慢,在这里我并不推荐。

简单说一下原理。
通过dfs找出当前节点的每个子树的序列表示,再对所有子树进行排序,再将排序后的序列相加来作为当前根树的序列表示。

以POJ 1635 为例的代码 570 ms

#include <algorithm>
#include <queue>
#include <vector>
#include <iostream>
#include <string>

using namespace std;
const int maxn = 1e6 + 5;

#define each(i, n) for (int(i) = 0; (i) < (n); (i)++)
#define reach(i, n) for (int(i) = n - 1; (i) >= 0; (i)--)
#define range(i, st, en) for (int(i) = (st); (i) <= (en); (i)++)
#define rrange(i, st, en) for (int(i) = (en); (i) >= (st); (i)--)
#define fill(ary, num) memset((ary), (num), sizeof(ary))

string treeMinR(int pos, const string& str)
{
    int len = str.length();
    vector<string> vec;
    while (pos < len && str[pos] == '0') {
        vec.push_back('0' + treeMinR(pos + 1, str));
        pos += vec.back().size();
    }
    sort(vec.begin(), vec.end());
    string res = "";
    each(i, (int)vec.size()) res += vec[i];
    return res + '1';
}

int main()
{
    ios::sync_with_stdio(false);
    int T;
    string a, b;
    cin >> T;
    while (T--) {
        cin >> a >> b;
        if (treeMinR(0, a) == treeMinR(0, b))
            cout << "same" << endl;
        else
            cout << "different" << endl;
    }
    return 0;
}

Hash 解法

这是杨戈的Hash论文在实际题目的应用,判断树的同构。

首先我们必须认识到,因为两棵同构树只是从一维上的顺序不同而已。如果对于一棵树,我们用一个在我们可允许范围的数字(也就是我们的Hash值)去表示他,那么一个节点的所有子树的Hash值之和,将与子树的顺序毫不相关!!

Hash的神奇与魅力!!!!

以POJ 1635 为例的代码示例 0ms

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>

using namespace std;
const int maxn = 5e3 + 5;
const int mod = 11257;

#define each(i, n) for (int(i) = 0; (i) < (n); (i)++)
#define reach(i, n) for (int(i) = n - 1; (i) >= 0; (i)--)
#define range(i, st, en) for (int(i) = (st); (i) <= (en); (i)++)
#define rrange(i, st, en) for (int(i) = (en); (i) >= (st); (i)--)
#define fill(ary, num) memset((ary), (num), sizeof(ary))

int val[maxn], len;
char a[maxn], b[maxn], *it;

int Hash(int pos)
{
    int sum = val[pos];
    while (it && *it++ == '0')
        sum = (sum + val[pos] * Hash(pos + 1)) % mod;
    return sum * sum % mod;
}

int main()
{
    each(i, 5000) val[i] = rand() % mod;
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%s %s", a, b);
        it = a, len = strlen(a);
        int ha = Hash(0);
        it = b, len = strlen(b);
        int hb = Hash(0);
        puts(ha == hb ? "same" : "different");
    }
    return 0;
}

鉴于这等差距,我决定于今日就开启我的Hash专题。