3331. 修改后子树的大小

简介: 【10月更文挑战第11天】3331. 修改后子树的大小

3331. 修改后子树的大小

找到距离节点x最近的祖先节点y,且s[x]==s[y]。然后将x连到y上.

题解

维护这个换父节点这个要求,所以需要从顶到下(只执行一次)dfs。同时维护路径上的每个字母对应的最新节点,初始化成一个全为-1的数组,表示(a-z)都是没有最新出现的。递归到x节点时,更新最新出现的位置,a[s[x]]=x

对于节点x及其子节点y如果a[s[y]]!=-1(说明前面s[y]这个字符一定出现过)。把y这个节点连到a[s[y]]这节点上,成为其子节点。同时把x的子节点y改成-1(说明已经把y移除了)。在第二次dfs(记录结果)中,不去递归等于-1的儿子。

注意写回溯递归返回前,要恢复a[s[x]]的原始值(递归到x之前的值)

第二次dfs

写一个自底向上的DFS,返回子树的大小。

当前子树大小为1(当前节点),加上所有子树的大小之和。

class Solution {
public:
    void f(string& s,vector<int>& a,vector<vector<int> >& dp,int x){
        int sx= s[x]-'a';
        int old = a[sx];//为之后回溯做准备
        a[sx]=x;//上一个祖先更新
        for(int i=dp[x].size()-1;i>=0;i--){//遍历子节点
            int y=dp[x][i];//子元素
            int tmp=a[s[y]-'a'];//找这个元素之前出现过没
            if(tmp!=-1){//如果之前出现过这个元素。
                dp[tmp].push_back(y);//将这个放在tmp上
                dp[x][i]=-1;// 删除y
            }
            f(s,a,dp,y);
        }
        a[sx]=old;
    }
    vector<int> findSubtreeSizes(vector<int>& parent, string s) {
        int n=parent.size();
        vector<vector<int> > dp(n);
        for(int i=1;i<n;i++){//第一个根节点为-1,不用管。
            dp[parent[i]].push_back(i);//记录子节点
        }
        vector<int> a(26,-1);
        // function<void(int)> f = [&](int x){
        //     int sx= s[x]-'a';
        //     int old = a[sx];//为之后回溯做准备
        //     a[sx]=x;//上一个祖先更新
        //     for(int i=dp[x].size()-1;i>=0;i--){//遍历子节点
        //         int y=dp[x][i];//子元素
        //         int tmp=a[s[y]-'a'];//找这个元素之前出现过没
        //         if(tmp!=-1){//如果之前出现过这个元素。
        //             dp[tmp].push_back(y);//将这个放在tmp上
        //             dp[x][i]=-1;// 删除y
        //         }
        //         f(y);
        //     }
        //     a[sx]=old;
        // };    
        f(s,a,dp,0);
        vector<int> ans(n,1);//注意这里把当前节点算进去
        auto dfs = [&](auto&& dfs,int x) -> void {
            for(int y: dp[x]){
                if(y!=-1){
                    dfs(dfs,y);
                    ans[x]+=ans[y];
                }
            }
        };
        dfs(dfs,0);
        return ans;
    }
};

也可以直接一次dfs自底向上,就是在维护的同时维护树的大小。

如果将y连到a[s[y]]上,就将其树大小加到a[s[y]]

如果y没有连到a[s[y]]上,那么说明它还连在原本节点x上,也就加到x上。

最后别忘了回溯

class Solution {
public:
    vector<int> findSubtreeSizes(vector<int>& parent, string s) {
        int n=parent.size();
        vector<vector<int> > dp(n);
        for(int i=1;i<n;i++){//第一个根节点为-1,不用管。
            dp[parent[i]].push_back(i);//记录子节点
        }
        vector<int> ans(n,1);//注意这里把当前节点算进去
        vector<int> a(26,-1);

        auto dfs = [&](auto&& dfs,int x) -> void {
            int sx= s[x]-'a';
            int old = a[sx];//为之后回溯做准备
            a[sx]=x;//上一个祖先更新
            for(int y: dp[x]){
                dfs(dfs,y);
                int tmp=a[s[y]-'a'];
                if(tmp==-1){//没出现过
                    ans[x]+=ans[y];//就是保持不动还连在原本的父节点上
                }else {
                    ans[tmp]+=ans[y];//换父节点
                }
            }
            a[sx]=old;
        };
        dfs(dfs,0);
        return ans;
    }
};
目录
相关文章
|
10月前
|
运维
【10月更文挑战赛】获奖名单出炉,快来看看谁是十月创作明星!
【10月更文挑战赛】获奖名单出炉,快来看看谁是十月创作明星!
309 9
|
10月前
|
运维 负载均衡 安全
|
11月前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
2557 44
|
11月前
|
druid Java Maven
|
10月前
|
数据采集 搜索推荐 UED
异步渲染对 SEO 的影响及应对策略
【10月更文挑战第23天】异步渲染在提升用户体验和性能的同时,确实会给 SEO 带来一定的挑战。但通过合理运用预渲染、提供静态版本、设置爬虫抓取时间、优化页面结构和内容以及使用服务端渲染等策略,可以有效地解决这些问题,实现 SEO 和用户体验的双赢。在未来的网页开发中,我们需要更加注重异步渲染技术与 SEO 的协调发展,以适应不断变化的网络环境和用户需求。
|
10月前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
10月前
|
监控 前端开发 JavaScript
React 静态网站生成工具 Next.js 入门指南
【10月更文挑战第20天】Next.js 是一个基于 React 的服务器端渲染框架,由 Vercel 开发。本文从基础概念出发,逐步探讨 Next.js 的常见问题、易错点及解决方法,并通过具体代码示例进行说明,帮助开发者快速构建高性能的 Web 应用。
452 10
|
10月前
|
传感器 XML IDE
探索安卓应用开发:从基础到进阶
【10月更文挑战第23天】在数字化时代的浪潮中,移动应用已成为人们日常生活的延伸。本文以安卓平台为例,深入浅出地介绍了如何从零开始构建一个安卓应用,涵盖了开发环境搭建、基本组件使用、界面设计原则以及进阶技巧等关键步骤。通过实例演示和代码片段,引导读者逐步掌握安卓应用开发的核心技能,旨在激发更多开发者对安卓平台的探索热情,并为初学者提供一条清晰的学习路径。
|
10月前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
255 9
|
10月前
|
缓存 监控 数据挖掘
C# 一分钟浅谈:性能测试与压力测试
【10月更文挑战第20天】本文介绍了性能测试和压力测试的基础概念、目的、方法及常见问题与解决策略。性能测试关注系统在正常条件下的响应时间和资源利用率,而压力测试则在超出正常条件的情况下测试系统的极限和潜在瓶颈。文章通过具体的C#代码示例,详细探讨了忽视预热阶段、不合理测试数据和缺乏详细监控等常见问题及其解决方案,并提供了如何避免这些问题的建议。
233 7