当树为无根树时,可以枚举每一的树的节点作为根,然后判相应的有根树的同构了.
这样问题就是怎么判断两个有根树是否同构.
解法1 : 暴力枚举,就是对于两棵树,递归的比较两个数的子树,如果存在两个树的子树之间的某一个一一配对,使所有相应的子树都同构,那么这两棵树同构.
附代码:[POJ_1635]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //树的最小表示,以及判定有根树是否同构
2 //其实就是构造出来树,然后再递归的比较一下,或许是数据弱
3 #include <iostream>
4 #include <cstring>
5 #include <algorithm>
6 #include <climits>
7 #include <cstdio>
8 #include <cmath>
9
10 using namespace std;
11
12 int tree1[1510][1510];
13 int tree2[1510][1510];
14 int len1[1510];
15 int len2[1510];
16 char str1[3100],str2[3100];
17 int stk[1510];
18 int cnt1[1510], cnt2[1510];
19 bool vis[1510];
20
21 inline bool cmp1(int a, int b)
22 {
23 if(cnt1[a] != cnt1[b])
24 return cnt1[a] < cnt1[b];
25
26 return len1[a] < len1[b];
27 }
28
29 inline bool cmp2(int a, int b)
30 {
31 if(cnt2[a] !=cnt2[b])
32 return cnt2[a] < cnt2[b];
33 return len2[a] < len2[b];
34 }
35
36 void DFS1(int x)
37 {
38 cnt1[x] = 1;
39 for(int i = 0; i < len1[x]; i++)
40 {
41 DFS1(tree1[x][i]);
42 cnt1[x] += cnt1[tree1[x][i]];
43 }
44 }
45 void DFS2(int x)
46 {
47 cnt2[x] = 1;
48 for(int i = 0; i < len2[x]; i++)
49 {
50 DFS2(tree2[x][i]);
51 cnt2[x] += cnt2[tree2[x][i]];
52 }
53 }
54
55 void build()
56 {
57 int l = strlen(str1);
58 memset(len1, 0, sizeof(len1));
59 memset(len2, 0, sizeof(len2));
60 int cur = 0;
61 int cnt = 0;
62 int top = 0;
63 for(int i = 0; i < l; i++)
64 {
65 switch(str1[i])
66 {
67 case '0':
68 {
69 stk[++top] = cur;
70 tree1[cur][len1[cur]++] = ++cnt;
71 cur = cnt;
72 break;
73 }
74 case '1':
75 {
76 cur = stk[top--];
77 break;
78 }
79 }
80 }
81 cur = 0;
82 cnt = 0;
83 top = 0;
84 for(int i = 0; i < l; i++)
85 {
86 switch(str2[i])
87 {
88 case '0':
89 {
90 stk[++top] = cur;
91 tree2[cur][len2[cur]++] = ++cnt;
92 cur = cnt;
93 break;
94 }
95 case '1':
96 {
97 cur = stk[top--];
98 break;
99 }
100 }
101 }
102 memset(cnt1, 0, sizeof(cnt1));
103 memset(cnt2, 0, sizeof(cnt2));
104 DFS1(0);
105 DFS2(0);
106 }
107
108 bool CMP(int a, int b)
109 {
110 if(len1[a] != len2[b] || cnt1[a] != cnt2[b])
111 {
112 return false;
113 }
114 int x, y;
115
116 for(int i = 0; i < len1[a]; i++)
117 {
118 bool bingo = false;
119 x = tree1[a][i];
120 for(int j = 0; j < len2[b]; j++)
121 {
122 y = tree2[b][j];
123 if(vis[y])
124 continue;
125 if(len1[x] != len2[y] || cnt1[x] != cnt2[y])
126 continue;
127 if(CMP(x, y))
128 {
129 vis[y] = true;
130 bingo = true;
131 break;
132 }
133 }
134 if (!bingo)
135 {
136 for(int j = 0; j < len2[b]; j++)
137 {
138 vis[tree2[b][j]] = false;
139 }
140 return false;
141 }
142 }
143 for(int j = 0; j < len2[b]; j++)
144 {
145 vis[tree2[b][j]] = false;
146 }
147 return true;
148 }
149
150 int main()
151 {
152 freopen("input.txt", "r", stdin);
153
154 int T;
155 gets(str1);
156 sscanf(str1, "%d", &T);
157 while (T--)
158 {
159 gets(str1);
160 gets(str2);
161 if(strlen(str1) != strlen(str2))
162 {
163 puts("different");
164 continue;
165 }
166
167 build();
168 memset(vis, false, sizeof(vis));
169 if (CMP(0, 0))
170 {
171 puts("same");
172 }
173 else
174 {
175 puts("different");
176 }
177 }
178 return 0;
179 }
解法2:hash,算法描述见07年国家集训队论文-杨弋《Hash在信息学竞赛中的一类应用》
附代码:[POJ_1635]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream>
2 #include <fstream>
3 #include <algorithm>
4 #include <cstring>
5 #include <climits>
6 #include <cmath>
7
8 using namespace std;
9
10 int h[11000];
11 char str1[3100], str2[3100];
12 char *p;
13
14 int Hash(int j)
15 {
16 int sum = h[j + 5000];//这里的j是记录的节点度
17 while(*p && *p++ == '0')//这个巧妙的循环,把子节点的hash值都加给了父节点,作为父节点的hash值
18 {
19 sum = (sum + h[j] * Hash(j + 1)) % 19001;
20 }
21 return (sum * sum) % 19001;
22 }
23
24 inline void init()
25 {
26 for(int i = 0; i < 10000; i++)
27 h[i] = (rand() %19901);
28 }
29
30 int main()
31 {
32 // freopen("input.txt", "r", stdin);
33 int T;
34 scanf("%d", &T);
35 init();
36 while(T--)
37 {
38 scanf("%s%s", str1, str2);
39 p = str1;
40 int a = Hash(1);
41 p = str2;
42 int b = Hash(1);
43
44 if(a == b)
45 {
46 puts("same");
47 }
48 else
49 {
50 puts("different");
51 }
52 }
53 return 0;
54 }
解法3:这种解法是从网上看到的,思路:一棵树统计每个节点的子节点个数,每个节点的深度,最后给每个节点排序,即可当成树的最小表示。最小表示相同的树同构。
但是这个解法是有错误的,已经被GG牛给cha掉了,
附上被cha的代码:[POJ_1635]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream>
2 using namespace std;
3 const int maxn=3005;
4 struct node
5 {
6 int depth,son;
7 }tree[maxn],tree2[maxn];
8 int CMP(const void* a,const void* b)
9 {
10 node* p=(node*)a;
11 node* q=(node*)b;
12 if(p->depth==q->depth) return p->son-q->son;
13 else return p->depth-q->depth;
14 }
15 int min_pre(char str[],node result[])//无回路的图,选定根节点,即可确定一棵树。一棵树统计每个节点的子节点个数,每个节点的深度,最后排序,即可当成树的最小表示。最小表示相同的树同构。
16 {
17 int node_num=1,now=0;
18 int father[maxn];
19 father[0]=result[0].son=result[0].depth=0;
20 for(int i=0;str[i];i++)
21 {
22 if(str[i]=='0')
23 {
24 father[node_num]=now;
25 result[node_num].depth=result[father[node_num]].depth+1;
26 result[node_num].son=0;
27 now=node_num++;
28 }
29 else
30 {
31 result[father[now]].son+=result[now].son+1;
32 now=father[now];
33 }
34 }
35 qsort(result,node_num,sizeof(result[0]),CMP);
36 return node_num;
37 }
38 int main()
39 {
40 char str[maxn];
41 int T;
42 scanf("%d",&T);
43 for(int t=0;t<T;t++)
44 {
45 scanf("%s",str);
46 int num=min_pre(str,tree);
47 scanf("%s",str);
48 int num2=min_pre(str,tree2);
49 if(num!=num2)
50 {
51 printf("different\n");
52 continue;
53 }
54 int i;
55 for(i=0;i<num;i++)
56 {
57 if(tree[i].depth!=tree2[i].depth||tree[i].son!=tree2[i].son)
58 {
59 printf("different\n");
60 break;
61 }
62 }
63 if(i>=num) printf("same\n");
64 }
65 return 0;
66 }
cha的数据:
1
00101011000111
00100111001011
然后我修改了一下节点深度的设置,又被cha掉,至此死心.
反思是,这个算法是变相的hash,但是准确程度远不及算法2.
Thx to GG牛.