1233. Remove Sub-Folders from the Filesystem

Given a list of folders folder, return the folders after removing all sub-folders in those folders. You may return the answer in any order.

If a folder[i] is located within another folder[j], it is called a sub-folder of it. A sub-folder of folder[j] must start with folder[j], followed by a "/". For example, "/a/b" is a sub-folder of "/a", but "/b" is not a sub-folder of "/a/b/c".

The format of a path is one or more concatenated strings of the form: '/' followed by one or more lowercase English letters.

  • For example, "/leetcode" and "/leetcode/problems" are valid paths while an empty string and "/" are not.

Example 1:

Input: folder = ["/a","/a/b","/c/d","/c/d/e","/c/f"]
Output: ["/a","/c/d","/c/f"]
Explanation: Folders "/a/b" is a subfolder of "/a" and "/c/d/e" is inside of folder "/c/d" in our filesystem.

Example 2:

Input: folder = ["/a","/a/b/c","/a/b/d"]
Output: ["/a"]
Explanation: Folders "/a/b/c" and "/a/b/d" will be removed because they are subfolders of "/a".

Example 3:

Input: folder = ["/a/b/c","/a/b/ca","/a/b/d"]
Output: ["/a/b/c","/a/b/ca","/a/b/d"]

Constraints:

  • 1 <= folder.length <= 4 * 104
  • 2 <= folder[i].length <= 100
  • folder[i] contains only lowercase letters and '/'.
  • folder[i] always starts with the character '/'.
  • Each folder name is unique.

Approach 01:

#include <bits/stdc++.h>

using namespace std;

class Solution {
public:
    vector<string> removeSubfolders(vector<string>& folderPaths) {
        vector<string> result;
        string previousFolder;

        ranges::sort(folderPaths);

        for (const string& currentFolder : folderPaths) {
            if (!previousFolder.empty() && currentFolder.find(previousFolder) == 0 && currentFolder[previousFolder.length()] == '/')
                continue;
            result.push_back(currentFolder);
            previousFolder = currentFolder;
        }

        return result;
    }
};
class Solution:
    def removeSubfolders(self, folderPaths: list[str]) -> list[str]:
        result = []
        previousFolder = ""

        folderPaths.sort()

        for currentFolder in folderPaths:
            if previousFolder and currentFolder.startswith(previousFolder) and currentFolder[len(previousFolder)] == '/':
                continue
            result.append(currentFolder)
            previousFolder = currentFolder

        return result

Time Complexity

  • Sorting:

    The folderPaths vector is sorted lexicographically using ranges::sort, which takes \(O(n \log n)\), where n is the total number of folder paths.

  • Looping through folder paths:

    After sorting, the algorithm iterates through each folder path, checking whether the current folder starts with the previous folder as a prefix. This comparison step takes \(O(L)\), where L is the average length of the folder path. Thus, the loop runs in \(O(n \cdot L)\), where \(L\) is the average string length, and n is the number of folder paths.

  • Overall Time Complexity:

    The overall time complexity is \(O(n \log n + n \cdot L)\). Since the \(n \log n\) term typically dominates, the final complexity is \(O(n \log n)\).

Space Complexity

  • Sorting:

    The sorting operation requires \(O(n)\) additional space for sorting the folder paths in place.

  • Result Vector:

    The result vector stores all the non-subfolder paths, requiring \(O(n)\) space in the worst case (if no subfolders are removed).

  • Overall Space Complexity:

    The space complexity is \(O(n + L)\), where n is the number of folder paths and L is the average length of the strings.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top