1233. 删除子文件夹

发布时间 2023-04-07 20:16:55作者: lxy_cn

题目链接:1233. 删除子文件夹

方法一:排序 + 循环

解题思路

先对 \(folder\) 数组根据字典序进行排序,排序完成后,扫描 \(folder\) 数组。由于在同一个高层目录下的文件夹在同一段区域,那么这一段区域的第一个文件夹就是这一系列文件夹的最高层目录 \((high)\),将其加入结果数组中。当出现以下情况之一时,表明扫描到下一区域:

  • 当前文件名长度 \(< high.size()\) 时,因为子文件夹名长度一定大于其高层文件夹名;
  • \(high\) 不是当前文件夹名的前缀;
  • 当前文件夹名的第 \(high.size()\)(从 \(0\) 开始)个字符 != '/',一定不是其子文件夹。

代码

class Solution {
public:
    vector<string> removeSubfolders(vector<string>& folder) {
        sort(folder.begin(), folder.end());
        vector<string> ans{folder[0]};
        for (int i = 1; i < folder.size(); i ++ ) {
            int pre = (*ans.rbegin()).size();
            if (pre >= folder[i].size() || *ans.rbegin() != folder[i].substr(0, pre) || folder[i][pre] != '/') ans.emplace_back(folder[i]);
        }
        return ans;
    }
};

复杂度分析

时间复杂度:\(O(nlogn)\)
空间复杂度:\(O(1)\)

方法二:字典树

解题思路

参考-JDFZ 2019级 蒟蒻OIer:字典树(Trie)详解

代码

class Trie {
private:
    unordered_map<string, Trie*> children;
    int fid = -1; // fid != -1时,表示此节点为某字符串的终点,存储当前字符串在folder中的idx,方便取出整个字符串

public:
    vector<string> split(string &str) { // 将str中'/'去掉,将str分为一个个节点
        vector<string> res;
        int len = str.length();
        int l = 1, r = str.find_first_of('/', l); // 从idx=l开始,查询'/'下标
        while (r != string::npos) {
            res.push_back(str.substr(l, r - l));
            l = r + 1;
            r = str.find_first_of('/', l);
        }
        res.push_back(str.substr(l));
        return res;
    }

    void insert(int fid, string &str) {
        Trie* node = this; // 方便后续节点插入操作
        vector<string> ps = split(str);
        for (auto &c : ps) {
            if (!node->children.count(c)) { // 该子节点未被加入到trie中
                node->children[c] = new Trie();
            }
            node = node->children[c]; // 变更父节点,从而插入下一个节点
        }
        node->fid = fid; // 到达该字符串终点,设置fid
    }

    vector<string> search(vector<string>& folder) {
        vector<string> ans;
        function<void(Trie*)> dfs = [&](Trie* root) { //function<>特性 + lambda表达式
            if (root->fid != -1) { // 到达该分支第一个字符串终点,后面为该文件目录的子文件夹
                ans.push_back(folder[root->fid]);
                return;
            }
            for (auto& [str, child] : root->children) dfs(child);
        };
        dfs(this);
        return ans;
    }
};

class Solution {
public:
    vector<string> removeSubfolders(vector<string>& folder) {
        Trie* trie = new Trie();
        for (int i = 0; i < folder.size(); i ++ ) {
            trie->insert(i, folder[i]);
        }
        return trie->search(folder);
    }
};

复杂度分析

时间复杂度:\(O(n * m),n = folder.length(),m = max(folder[0...n-1].length())\)
空间复杂度:\(O(n * m)\)