find-like)Build an in-memory file lookup tool similar to the Unix find command.
The system should support two initial search criteria and be easily extensible so new search criteria can be added later.
Use the Strategy Design Pattern to implement the search criteria.
Supported Search Criteria
Search by Minimum File Size
Return all files strictly larger than a given size (in MB) under a specified directory recursively.
Search by File Extension
Return all files with a given file extension under a specified directory recursively.
Example: .xml, .jpg
Use Java-style naming conventions.
void putFile(String path, int sizeMb)
path will always be a valid absolute file path.sizeMb >= 0List<String> search(int searchCriteriaId, String dirPath, String args)
Find files matching a Search Criteria.
| Criteria | searchCriteriaId | args format | Example |
|---|---|---|---|
| Minimum File Size | 1 | "minSizeMb" | "5" |
| File Extension | 2 | ".ext" | ".xml" |
Examples:
search(1, "/data", "8")
search(2, "/work", ".xml")
Use the Strategy Design Pattern for implementing search criteria.
This ensures the system can easily support new search criteria in the future, such as:
FileSearch s = new FileSearch();
s.putFile("/data/pics/photoA.jpg", 4);
s.putFile("/data/pics/movie.mp4", 12);
s.putFile("/work/docs/readme.md", 1);
s.putFile("/work/docs/report.xml", 7);
Update file size:
s.putFile("/data/pics/photoA.jpg", 9);
Files > 8 MB inside /data
s.search(1, "/data", "8");
Result:
[
"/data/pics/photoA.jpg",
"/data/pics/movie.mp4"
]
Files with .xml inside /work
s.search(2, "/work", ".xml");
Result:
[
"/work/docs/report.xml"
]
FileSearch s = new FileSearch();
s.putFile("/media/images/aa.jpg", 6);
s.putFile("/media/images/ab.jpg", 7);
s.putFile("/media/images/ac.xml", 2);
s.putFile("/office/reports/r1.xml", 9);
s.putFile("/office/reports/r2.xml", 4);
s.putFile("/office/notes.txt", 3);
Files > 5 MB under /media/images
s.search(1, "/media/images", "5");
Result:
[
"/media/images/aa.jpg",
"/media/images/ab.jpg"
]
Files with .xml under /office (recursive)
s.search(2, "/office", ".xml");
Result:
[
"/office/reports/r1.xml",
"/office/reports/r2.xml"
]
putFile and search/)/sizeMb range: 0 → 10000search must traverse all nested directories recursivelyThis is a classic LLD + Strategy Pattern question. The interviewer checks:
I'll show a production-quality C++ design.
We separate the system into three responsibilities:
Stores files in memory.
path -> size
Each search criteria implements a strategy interface.
SearchStrategy
↑
SizeSearchStrategy
ExtensionSearchStrategy
Handles:
Without strategy:
if(criteria == 1) ...
else if(criteria == 2) ...
Every new feature requires modifying the main logic ❌
With strategy:
strategy->match(file)
Add new search criteria without touching existing code ✅
Example future extension:
NameContainsStrategy
CreatedAfterStrategy
OwnerStrategy
struct File {
string path;
int sizeMb;
};
Storage:
unordered_map<string, int> files
Key = path Value = size
class SearchStrategy {
public:
virtual bool match(const string& path, int sizeMb, const string& args) = 0;
virtual ~SearchStrategy() {}
};
Criteria: size > minSize
class SizeSearchStrategy : public SearchStrategy {
public:
bool match(const string& path, int sizeMb, const string& args) override {
int minSize = stoi(args);
return sizeMb > minSize;
}
};
Criteria: file ends with extension
class ExtensionSearchStrategy : public SearchStrategy {
public:
bool match(const string& path, int sizeMb, const string& args) override {
string ext = args;
if (path.size() < ext.size()) return false;
return path.substr(path.size() - ext.size()) == ext;
}
};
Key responsibilities:
#include <bits/stdc++.h>
using namespace std;
struct File {
string path;
int sizeMb;
};
class SearchStrategy {
public:
virtual bool match(const string& path, int sizeMb, const string& args) = 0;
virtual ~SearchStrategy() {}
};
class SizeSearchStrategy : public SearchStrategy {
public:
bool match(const string& path, int sizeMb, const string& args) override {
int minSize = stoi(args);
return sizeMb > minSize;
}
};
class ExtensionSearchStrategy : public SearchStrategy {
public:
bool match(const string& path, int sizeMb, const string& args) override {
if (path.size() < args.size()) return false;
return path.substr(path.size() - args.size()) == args;
}
};
class FileStorage {
private:
unordered_map<string, int> files;
public:
void putFile(const string& path, int sizeMb) {
files[path] = sizeMb;
}
const unordered_map<string, int>& getAllFiles() const {
return files;
}
};
class FileSearch {
private:
FileStorage storage;
unordered_map<int, SearchStrategy*> strategies;
bool isUnderDirectory(const string& path, const string& dir) {
if (path.size() < dir.size()) return false;
if (path.substr(0, dir.size()) != dir) return false;
if (path.size() == dir.size()) return true;
return path[dir.size()] == '/';
}
public:
FileSearch() {
strategies[1] = new SizeSearchStrategy();
strategies[2] = new ExtensionSearchStrategy();
}
void putFile(string path, int sizeMb) {
storage.putFile(path, sizeMb);
}
vector<string> search(int searchCriteriaId, string dirPath, string args) {
vector<string> result;
if (!strategies.count(searchCriteriaId)) return result;
SearchStrategy* strategy = strategies[searchCriteriaId];
auto& files = storage.getAllFiles();
for (auto& [path, size] : files) {
if (!isUnderDirectory(path, dirPath)) continue;
if (strategy->match(path, size, args))
result.push_back(path);
}
sort(result.begin(), result.end());
return result;
}
~FileSearch() {
for (auto& [id, strategy] : strategies)
delete strategy;
}
};
int main() {
FileSearch fs;
// Adding files
fs.putFile("/office/reports/a.xml", 5);
fs.putFile("/office/reports/b.xml", 15);
fs.putFile("/office/reports/c.txt", 20);
fs.putFile("/office/data/d.xml", 25);
fs.putFile("/home/user/e.xml", 30);
cout << "---- Search: Size > 10 in /office ----\n";
vector<string> res1 = fs.search(1, "/office", "10");
for (auto& file : res1) {
cout << file << "\n";
}
cout << "\n---- Search: Extension .xml in /office/reports ----\n";
vector<string> res2 = fs.search(2, "/office/reports", ".xml");
for (auto& file : res2) {
cout << file << "\n";
}
return 0;
}
Let:
N = number of files (≤2000)
O(1)
O(N log N)
Perfect for constraints.
Open/Closed Principle
The system is open for extension but closed for modification.
To add new criteria:
class NameContainsStrategy : public SearchStrategy
Then just register:
strategies[3] = new NameContainsStrategy();
No other code changes required.
search(3, "/data", "report")
size > 10 AND extension = ".xml"
Use directory index (Trie) instead of scanning all files.