杭电多校6 1008
题意:
给定两棵树,问树
A
A
A中存在多少个与树
B
B
B结构相同的子图
题解:
对于二叉树结构匹配这个问题,我们可以联想到用
01
01
01字符串来简化,题意保证树
B
B
B的叶子节点不超过
20
20
20,那么意味着我们可以将树
B
B
B分解为不超过
20
20
20个
01
01
01字符串,然后拿这
20
20
20个
01
01
01字符串在树
A
A
A上进行匹配,是一种接近于多模式串匹配的问题,多模式串匹配显然可以考虑到
A
C
A
M
ACAM
ACAM,
A
C
A
M
ACAM
ACAM是用来处理多模式串匹配问题的。
对于树
B
B
B,我们考虑分离出所有
01
01
01字符串,然后对其构建
A
C
A
M
ACAM
ACAM,下图为树
B
B
B的一种结构例子,其中
3
,
7
,
8
,
5
,
9
,
10
3,7,8,5,9,10
3,7,8,5,9,10均为叶子节点,即存在失配情况,因此将其指向失配指针,相当于为其再次构建两个儿子,这两个儿子正是失配指针指向的节点的两个儿子,如果存在一个叶子节点,其失配指针也指向一个叶子节点,那么我们就继续失配,直到找到一个节点存在两个儿子为止。那么构建出来的
A
C
A
M
ACAM
ACAM即下图所示,下划线 + 数字即表示失配再构建的儿子,这样的树形结构就可以保证无论如何向左或向右移动,必然存在一个节点与之对应。
现在考虑在树
A
A
A上进行
D
F
S
DFS
DFS,并保证树
B
B
B的
A
C
A
M
ACAM
ACAM与之移动同步,那么每个树
A
A
A的节点必然对应一个树
B
B
B的(扩展)节点。然后我们再考虑如何计算答案,对于
D
F
S
DFS
DFS过程中,树
A
A
A的每一个节点都有一个扩展节点与之对应,那么如果在树
A
A
A中,树
B
B
B同步走到叶子节点,那么必然可以保证该叶子节点及以上的结构都存在,所以我们只需要当
D
F
S
DFS
DFS时,判断树
A
A
A当前在树
B
B
B中对应的节点是否为叶子节点即可,同时,我们维护
D
F
S
DFS
DFS过程中两树的深度,对于树
A
A
A的某一个节点
u
u
u,若其对应树
B
B
B的叶子节点
v
v
v,那么树
A
A
A中当前节点
u
u
u对应树
B
B
B的根节点深度为
d
e
p
u
−
d
e
p
v
dep_u - dep_v
depu−depv,因为是
D
F
S
DFS
DFS,所以树
A
A
A中当前到树
A
A
A根节点的路径必然是一条链,所以用一个栈来维护这条链即可,所以树
A
A
A中对应树
B
B
B的每一个根节点计数加一,如果某一个点
u
u
u的计数数量等于树
B
B
B的叶子节点数量,那么保证该点从树
B
B
B的根节点同步走,必然都能走到树
B
B
B所有叶子节点,因此计入答案。
还有一个点需要注意,对于下图中
0
−
2
−
5
和
1
−
4
−
7
0-2-5和1-4-7
0−2−5和1−4−7这两个相同的结构,
7
7
7同样也可以作为
5
5
5这个叶子节点让
1
1
1作为根节点计数
+
1
+1
+1,所以,对于失配指针指向叶子节点,我们在
D
F
S
DFS
DFS过程中,同样要走完这个失配指针,直到指向节点不再是叶子节点。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define debug(p) for (auto i : p)cerr << i << " "; cerr << endl;
#define debugs(p) for (auto i : p)cerr << i.first << " " << i.second << endl;
typedef pair<int, int> pll;
string yes = "YES";
string no = "NO";
constexpr int N = 3e5 * 20 + 7;
int n, m;
int leaf = 0;
int cnt[N];
vector<int>stk, ans;
struct ACAM{
int tr[N][2], cnt[N], ne[N];//trie树存储,模式串叶子结点计数, 失配指针
int idx = 0;
int next[N], tag[N];//指向叶子节点的失配指针,叶子结点的标记深度
int ls[N], rs[N];//存二叉树结构
int n;//二叉树结构节点个数
string s;
void ins() //给定多个匹配串构建trie
{
int p = 0;
for (int i = 0; i < s.size(); i++) {
int u = s[i] - '0';
if (!tr[p][u])
tr[p][u] = ++idx;
p = tr[p][u];
}
cnt[p]++;
}
void ins2()//给定二叉树构建trie
{
for (int i = 0; i <= n; i++)tag[i] = -1;
queue<int>q;
q.push(0);
while(q.size())
{
int p = q.front();
q.pop();
if(ls[p])
{
tr[p][0] = ls[p];
q.push(ls[p]);
}
if(rs[p])
{
tr[p][1] = rs[p];
q.push(rs[p]);
}
}
}
void init()
{
for (int i = 0; i <= n; i++)
{
tr[i][0] = tr[i][1] = 0;
tag[i] = -1;
ls[i] = rs[i] = 0;
ne[i] = next[i] = 0;
}
}
void build() {
queue<int>q;
for (int i = 0; i < 2; i++) {
if (tr[0][i])
q.push(tr[0][i]);
}
while (q.size()) {
int p = q.front();
q.pop();
//枚举每个可能存在的子节点,如果存在就添加虚边并加入队列,不存在就维护节点
if(tag[ne[p]] >= 0)next[p] = ne[p];//存下失配指针指向叶子节点的失配方向
else next[p] = next[ne[p]];
for (int i = 0; i < 2; i++) {
int s = tr[p][i];
if (tr[p][i]) {
q.push(s);
ne[s] = tr[ne[p]][i];//子节点的虚边连向父节点虚边指向的另一个父节点的子节点
} else
tr[p][i] = tr[ne[p]][i];
}
}
}
}s, t;
void dfs1(int u, int depth)
{
if(!t.tr[u][0] && !t.tr[u][1])
{
t.tag[u] = depth;
leaf++;
return;
}
if(t.tr[u][0])dfs1(t.tr[u][0], depth + 1);
if(t.tr[u][1])dfs1(t.tr[u][1], depth + 1);
}
void dfs2(int p1, int p2)
{
stk.push_back(p1);
if(t.tag[p2] >= 0) cnt[stk[(int)stk.size() - t.tag[p2] - 1]]++;
for (int i = t.next[p2]; i > 0; i = t.next[i]) cnt[stk[(int)stk.size() - t.tag[i] - 1]]++;
if(s.tr[p1][0])dfs2(s.tr[p1][0], t.tr[p2][0]);
if(s.tr[p1][1])dfs2(s.tr[p1][1], t.tr[p2][1]);
if(cnt[p1] == leaf)ans.push_back(p1);
stk.pop_back();
}
void init()
{
leaf = 0;
stk.clear();
ans.clear();
s.init();
t.init();
for (int i = 0; i <= n; i++) cnt[i] = 0;
}
void solve()
{
cin >> n;
s.n = n;
s.init();
for (int i = 0; i < n; i++) cin >> s.ls[i] >> s.rs[i];
cin >> m;
t.n = m;
t.init();
for (int i = 0; i < m; i++) cin >> t.ls[i] >> t.rs[i];
s.ins2();
t.ins2();
dfs1(0, 0);
t.build();
dfs2(0, 0);
cout << ans.size() << endl;
if(ans.empty())cout << endl;
else
{
sort(ans.begin(), ans.end());
for (int i = 0; i < ans.size(); i++) cout << ans[i] << " \n"[i == ans.size() - 1];
}
init();
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while(T--)
{
solve();
}
}