Skip to main content Link Menu Expand (external link) Document Search Copy Copied

9429. Directory

Code Battle (`23 동계 대학생 S/W 알고리즘 특강 기초학습 문제)


본 게시글은 개인 학습 용도로 작성한 게시글입니다.

(문제 출처: SW Expert Academy)


  • 트리 구조로 디렉터리 구조를 구현.
  • 제약사항을 분석하여 함수별로 목표로 할 시간복잡도를 정하고 풀이를 시작함.
    • 테스트케이스당 $50,000$번까지 호출이 있을 수 있고, 이 중에서 cmd_cp()의 호출 횟수는 $10\%$ 이하를 차지한다. 총 10개의 테스트케이스가 있으므로 함수 별 호출 횟수를 대략적으로 유추해본다면 다음과 같다:
      • init(): $10$회
      • cmd_mkdir(): $(45,000 / 4) \times 10 = 112,500$회
      • cmd_rm(): $(45,000 / 4) \times 10 = 112,500$회
      • cmd_mv(): $(45,000 / 4) \times 10 = 112,500$회
      • cmd_find(): $(45,000 / 4) \times 10 = 112,500$회
      • cmd_cp(): $(5,000) \times 10 = 50,000$회
    • 테스트케이스당 생성될 수 있는 디렉토리의 개수 $N$은 최대 $50,000$이다.
    • 각 함수 별 목표로 할 시간복잡도를 다음과 같이 정하였다.
      • init(): $O(N)$ 이하
      • cmd_mkdir(): $O(\log{N})$
      • cmd_rm(): $O(\log{N})$
      • cmd_mv(): $O(\log{N})$
      • cmd_find(): $O(\log{N})$
      • cmd_cp(): $O(N)$ 이하
    • 5가지 함수 모두 주어진 경로를 이용하여 원하는 노드를 검색해 추가/수정/제거 작업을 해야한다. 이를 위해 find()를 정의했으며, 해당 함수는 $O(\log{N \times n(children)})$시간에 루트 노드로 부터 검색 대상인 노드를 찾아나간다. 자식노드의 개수는 최대 $30$개 이므로 사실상 $O(\log{N})$ 시간에 수행된다고 볼 수 있다.
    • cmd_find()에서는 하위 디렉토리의 개수(크기)를 구해야 한다.
      • 완전탐색을 통해 구하게 되면 $O(N)$시간이 걸리므로, 목표인 $O(\log{N})$에 수행할 수 없게 된다.
      • 디렉터리 구조가 수정될 때 각 노드의 크기를 미리 구해두는 방법을 사용한다. 자식노드의 추가/제거가 있을 때 마다 $O(\log{N})$시간에 조상 노드들의 크기를 갱신한다.
      • 조상노드에 빠르게 접근하기 위해, 각 노드에는 부모 노드를 가리키는 par 속성을 추가하여 사용한다.
    • 자식노드를 빠르게 생성하고 제거할 수 있도록 하기위해, 형제 노드간에 Double-linked list를 적용하였다. 형제간 노드의 삽입과 삭제를 $O(1)$에 수행할 수 있으며, 부모 노드는 연결 리스트의 HEAD만 알고 있으면 되므로 공간 효율도 좋다. 다만, 형제 노드의 삽입/제거 후 부모 노드의 크기를 갱신하는데 $O(\log{N})$ 시간이 걸리는 것에 유의.
    • cmd_mkdir()은 삽입, cmd_rm()는 제거, cmd_mv()는 제거+삽입, cmd_find()는 접근이다. 접근/삽입/제거는 $O(\log{N})$에 수행이 가능하다. cmd_cp()는 노드의 크기만큼 삽입을 반복하므로 $O(N)$의 시간이 걸리지만, 나머지 작업은 모두 $O(\log{N})$ 시간에 수행된다.

#include <iostream>

#define NAME_MINLEN 2
#define NAME_MAXLEN 6
#define PATH_MAXLEN 1999
#define DIRECTORY_MAXCREATE 50000
#define DIRECTORY_MAXCHILDREN 30

// The below commented functions are for your reference. If you want
// to use it, uncomment these functions.
int mstrcmp(const char *a, const char *b)
{
	int i;
	for (i = 0; a[i] != '\0'; i++)
	{
		if (a[i] != b[i])
			return a[i] - b[i];
	}
	return a[i] - b[i];
}

int mstrncmp(const char *a, const char *b, int len)
{
	for (int i = 0; i < len; i++)
	{
		if (a[i] != b[i])
			return a[i] - b[i];
	}
	return 0;
}

int mstrlen(const char *a)
{
	int len = 0;

	while (a[len] != '\0')
		len++;

	return len;
}

void mstrcpy(char *dest, const char *src)
{
	int i = 0;
	while (src[i] != '\0')
	{
		dest[i] = src[i];
		i++;
	}
	dest[i] = src[i];
}

void mstrncpy(char *dest, const char *src, int len)
{
	for (int i = 0; i<len; i++)
	{
		dest[i] = src[i];
	}
	dest[len] = '\0';
}

struct DirectoryNode {
    char name[NAME_MAXLEN+1];
    int size;
    DirectoryNode* par;
    DirectoryNode* children;
    DirectoryNode* prev;
    DirectoryNode* next;
};

DirectoryNode nodes[DIRECTORY_MAXCREATE+1];
DirectoryNode* new_node;
DirectoryNode* root;

void init(DirectoryNode* target, const char name[NAME_MAXLEN+1]) {
    target->size = 1;
    target->par = nullptr;
    target->children = nullptr;
    target->prev = nullptr;
    target->next = nullptr;
    mstrcpy(target->name, name);
}

DirectoryNode* create(const char name[NAME_MAXLEN+1]) {
    DirectoryNode* cur = new_node++;
    init(cur, name);
    return cur;
}

void addChild(DirectoryNode* parent, DirectoryNode* child) {
    if (parent->children != nullptr) {
        parent->children->prev = child;
    }
    child->par = parent;
    child->next = parent->children;
    parent->children = child;
    while (parent != nullptr) {
        parent->size += child->size;
        parent = parent->par;
    }
}

void removeChild(DirectoryNode* parent, DirectoryNode* child) {
    if (child == parent->children) {
        parent->children = child->next;
    }
    if (child->prev != nullptr) {
        child->prev->next = child->next;
    }
    if (child->next != nullptr) {
        child->next->prev = child->prev;
    }
    child->par = nullptr;
    child->prev = nullptr;
    child->next = nullptr;
    while (parent != nullptr) {
        parent->size -= child->size;
        parent = parent->par;
    }
}

void remove(DirectoryNode* target) {
    removeChild(target->par, target);
}

DirectoryNode* findChild(DirectoryNode* parent, const char name[NAME_MAXLEN+1]) {
    DirectoryNode* child = parent->children;
    while (child != nullptr) {
        if (mstrcmp(child->name, name) == 0) {
            return child;
        }
        child = child->next;
    }
    return nullptr;
}

DirectoryNode* ensureFindChild(DirectoryNode* parent, const char name[NAME_MAXLEN+1]) {
    DirectoryNode* child = findChild(parent, name);
    if (child == nullptr) {
        child = create(name);
        addChild(parent, child);
    }
    return child;
}

DirectoryNode* deepCopy(DirectoryNode* source) {
    DirectoryNode* par = create(source->name);
    for (DirectoryNode* child = source->children; child != nullptr; child = child->next) {
        addChild(par, deepCopy(child));
    }
    return par;
}

DirectoryNode* find(const char path[PATH_MAXLEN + 1]) {
    DirectoryNode* cur = root;
    char name[NAME_MAXLEN + 1] = { '\0' };
    const char *name_ptr = path;
    int name_len = 0;
    if (*name_ptr == '/') {
        name_ptr++;
    }
    while (*name_ptr != '\0') {
        if (*name_ptr != '/') {
            name[name_len++] = *name_ptr;
            name[name_len] = '\0';
        } else {
            cur = findChild(cur, name);
            name_len = 0;
        }
        name_ptr++;
    }
    return cur;
}

// n = ~50,000

// calls: 10
void init(int n) {
    new_node = nodes+1;
    root = nodes;
    init(root, "");
}

// calls: 112,500
// goal: O(log(N))
void cmd_mkdir(char path[PATH_MAXLEN + 1], char name[NAME_MAXLEN + 1]) {
    addChild(find(path), create(name));
}

// calls: 112,500
// goal: O(log(N))
void cmd_rm(char path[PATH_MAXLEN + 1]) {
    remove(find(path));
}

// calls: 50,000
// goal: O(N)
void cmd_cp(char srcPath[PATH_MAXLEN + 1], char dstPath[PATH_MAXLEN + 1]) {
    addChild(find(dstPath), deepCopy(find(srcPath)));
}

// calls: 112,500
// goal: O(log(N))
void cmd_mv(char srcPath[PATH_MAXLEN + 1], char dstPath[PATH_MAXLEN + 1]) {
    DirectoryNode* src = find(srcPath);
    removeChild(src->par, src);
    addChild(find(dstPath), src);
}

// calls: 112,500
// goal: O(log(N))
int cmd_find(char path[PATH_MAXLEN + 1]) {
	return find(path)->size - 1;
}

Back to top