LeetCode 3331. Find Subtree Sizes After Changes
biweekly contest 142。
題目
輸入 n 個節點的樹,編號從 0 到 n - 1,且根節點為 0。
以長度 n 的陣列 parent 表示,其中 parent[i] 代表節點 i 的副節點。
由於 0 是根節點,因此 parent[i] = -1。
另外輸入長度 n 的字串 s,其中 s[i] 代表節點 i 對應的字元。
對於編號 1 到 n - 1 的所有節點,我們同時進行以下操作一次:
- 找到最靠近節點 x,且具有相同字元的祖先節點 y,即 s[x] == s[y]。
- 若不存在 y,無事發生。
- 否則,將 x 與原父節點的連邊刪除,並在 x 和 y 之間連接一條邊,使 y 成為 x 的新父節點。
回傳長度 n 的陣列 answer,其中 answer[i] 代表操作後,以節點 i 為根的子樹的大小。
解法
根據題意模擬,構造出操作後的樹,然後求子樹大小。
先來一次 dfs 構造新樹。
每個節點的操作要同時進行,因此額外維護一個圖作為操作後的結果,而不直接修改原圖。
要找的 y 必須是父節點,因此在向子節點遞迴的時候需要以節點 x 與字元 c 對應。
但如果沒理解dfs 遍歷順序的同學可能就寫錯了。
節點 x 離開遞迴時,必須將 x 與 c 的對應恢復原狀,否則會影響到不相干的子樹!
而對應、取消的順序就與 dfs 相同,是後進先出,因此用堆疊實現剛剛好。
最後第二次 dfs 求子樹大小即可。
時間複雜度 O(N)。
空間複雜度 O(N)。
class Solution:
def findSubtreeSizes(self, parent: List[int], s: str) -> List[int]:
N = len(parent)
g = [[] for _ in range(N)]
g2 = [[] for _ in range(N)]
for i, fa in enumerate(parent):
if i > 0:
g[fa].append(i)
# rebuild tree
mp = defaultdict(list)
def dfs(i):
c = s[i]
if i > 0:
if mp[c]:
fa = mp[c][-1]
else:
fa = parent[i]
g2[fa].append(i)
# i as ancestor
mp[c].append(i)
for j in g[i]:
dfs(j)
# resotre
mp[c].pop()
dfs(0)
# count substree size
ans = [0] * N
def dfs2(i):
nonlocal ans
res = 1
for j in g2[i]:
res += dfs2(j)
ans[i] = res
return res
dfs2(0)
return ans