diff --git a/clone-graph/flynn.cpp b/clone-graph/flynn.cpp
new file mode 100644
index 000000000..39aa2df6f
--- /dev/null
+++ b/clone-graph/flynn.cpp
@@ -0,0 +1,67 @@
+/**
+ * 풀이
+ * - BFS와 해시맵을 사용하여 풀이합니다
+ * 
+ * Big O
+ * - N: 주어진 노드의 개수
+ * - E: 주어진 노드의 간선의 개수
+ * 
+ * - Time complexity: O(E)
+ *   - 한 Node에서 다른 Node로 향하는 모든 edge를 두번씩 탐색합니다 (두 방향으로 연결되어 있기 때문)
+ * - Space complexity: O(N) 
+ *   - 해시맵에 최대 N개의 노드를 저장합니다
+ */
+
+/*
+// Definition for a Node.
+class Node {
+public:
+    int val;
+    vector<Node*> neighbors;
+    Node() {
+        val = 0;
+        neighbors = vector<Node*>();
+    }
+    Node(int _val) {
+        val = _val;
+        neighbors = vector<Node*>();
+    }
+    Node(int _val, vector<Node*> _neighbors) {
+        val = _val;
+        neighbors = _neighbors;
+    }
+};
+*/
+
+class Solution {
+public:
+    Node* cloneGraph(Node* node) {
+        if (node == nullptr) return nullptr;
+
+        unordered_map<Node*, Node*> node_map;
+        node_map[node] = new Node(node->val);
+
+        queue<Node*> q;
+        q.push(node);
+
+        while (!q.empty()) {
+            Node* p = q.front();
+            q.pop();
+
+            for (Node* neighbor : p->neighbors) {
+                // 방문한 적이 없는 노드일 경우
+                if (node_map.find(neighbor) == node_map.end()) {
+                    // node_map에 새로운 노드를 복제하여 추가
+                    node_map[neighbor] = new Node(neighbor->val);
+
+                    // 큐에 추가
+                    q.push(neighbor);
+                }
+
+                node_map[p]->neighbors.push_back(node_map[neighbor]);
+            }
+        }
+
+        return node_map[node];
+    }
+};
diff --git a/longest-common-subsequence/flynn.cpp b/longest-common-subsequence/flynn.cpp
new file mode 100644
index 000000000..134f21156
--- /dev/null
+++ b/longest-common-subsequence/flynn.cpp
@@ -0,0 +1,77 @@
+/**
+ * 풀이 1
+ * - 2차원 DP를 사용하여 풀이합니다
+ *   DP[i][j]: text1의 i번째 문자까지와 text2의 j번째 문자까지 비교했을 때, 가장 긴 공통 부분 문자열의 길이
+ *             즉, text1[0 .. i - 1]와 text2[0 .. j - 1]의 가장 긴 공통 부분 문자열의 길이
+ *   DP[i][j] = if text1[i - 1] == text2[j - 1] then DP[i - 1][j - 1] + 1
+ *              else max(DP[i - 1][j], DP[i][j - 1])
+ * - 풀이 2로 공간복잡도를 줄일 수 있습니다
+ * 
+ * Big O
+ * - M: text1의 길이
+ * - N: text2의 길이
+ * 
+ * - Time complexity: O(N * M)
+ * - Space complexity: O(N * M)
+ */
+
+class Solution {
+public:
+    int longestCommonSubsequence(string text1, string text2) {
+        size_t m = text1.size();
+        size_t n = text2.size();
+
+        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
+
+        for (int i = 1; i <= m; ++i) {
+            for (int j = 1; j <= n; ++j) {
+                if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
+                else dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
+            }
+        }
+
+        return dp[m][n];
+    }
+};
+
+/**
+ * 풀이 2
+ * - 풀이 1의 DP 전개 과정을 보면 우리한테는 DP 배열 두 행만 필요하다는 걸 알 수 있습니다
+ * 
+ * Big O
+ * - M: text1의 길이
+ * - N: text2의 길이
+ * 
+ * - M >= N이 되도록 고릅니다
+ * 
+ * - Time complexity: O(N * M)
+ * - Space complexity: O(N)
+ */
+
+class Solution {
+public:
+    int longestCommonSubsequence(string text1, string text2) {
+        size_t m = text1.size();
+        size_t n = text2.size();
+
+        if (m < n) return longestCommonSubsequence(text2, text1);
+
+        vector<int> dp1(n + 1, 0);
+        vector<int> dp2(n + 1, 0);
+
+        for (int i = 1; i <= m; ++i) {
+            for (int j = 1; j <= n; ++j) {
+                if (text1[i - 1] == text2[j - 1]) dp2[j] = dp1[j - 1] + 1;
+                else dp2[j] = max(dp1[j], dp2[j - 1]);
+            }
+
+            if (i == m) break;
+
+            dp1.swap(dp2);
+            dp2.clear();
+            dp2.resize(n + 1, 0);
+        }
+
+        return dp2[n];
+    }
+};
diff --git a/longest-repeating-character-replacement/flynn.cpp b/longest-repeating-character-replacement/flynn.cpp
new file mode 100644
index 000000000..7a11ac80c
--- /dev/null
+++ b/longest-repeating-character-replacement/flynn.cpp
@@ -0,0 +1,73 @@
+/**
+ * 풀이
+ * - 주어진 s로 만들 수 있는 가장 긴 valid substring의 길이를 찾는 문제입니다
+ *   - valid substring: 최대 k개의 문자를 바꿔서, 모든 문자가 같게 만들 수 있는 substring
+ * 
+ * - 두 단계로 나누어 풀이에 대해 생각할 수 있습니다
+ * 
+ * - 1. 특정 길이의 valid substring을 만들 수 있는지 확인
+ *   - 함수 bool can_make_valid_substring(string const s, int substr_length, int k)
+ *   - 특정 길이의 substring에 대해서, 등장 빈도가 가장 높은 문자의 빈도수를 저장합니다 (max_freq)
+ *     만약 해당 substring이 valid substring이라면, max_freq + k >= substr_length 이어야 합니다
+ * 
+ * - 2. 최대 길이의 valid substring을 찾는다
+ *   - 이진탐색을 통해 최대 길이를 찾는다
+ *   - 함수 int characterReplacement(string s, int k)
+ *   - 주어진 문자열 s로 만들 수 있는 substring의 길이는 1이상 s.size() 이하입니다
+ *     이진 탐색의 범위를 위에서 언급한 대로 설정하고, 현재 탐색하려는 길이 (mid)에 대해서
+ *     can_make_valid_substring 함수를 호출하여 현재 길이로 valid substring을 만들 수 있는지 확인합니다
+ *     이진 탐색 알고리즘의 전개 및 결과에 대한 설명은 https://github.com/DaleStudy/leetcode-study/discussions/332를 참고해주세요 :)
+ * 
+ * Big O
+ * - N: 주어진 문자열 s의 길이
+ * - K: 주어진 정수 k
+ * 
+ * - Time complexity: O(N * logN)
+ * - Space complexity: O(1)
+ */
+
+class Solution {
+public:
+    bool can_make_valid_substring(string const s, int substr_length, int k) {
+        // 문자의 빈도수를 저장하는 배열입니다
+        array<int, 26> freq;
+        freq.fill(0);
+
+        // 최대 빈도수를 저장하는 변수입니다
+        int max_freq = 0;
+
+        int window_start = 0;
+
+        for (int window_end = 0; window_end < s.size(); ++window_end) {
+            ++freq[s[window_end] - 'A'];
+
+            int curr_size = window_end - window_start + 1;
+            if (curr_size > substr_length) {
+                --freq[s[window_start] - 'A'];
+                ++window_start;
+            }
+
+            max_freq = max(max_freq, freq[s[window_end] - 'A']);
+            if (max_freq + k >= substr_length) return true;
+        }
+
+        return false;
+    }
+
+    int characterReplacement(string s, int k) {
+        int lo = 1;
+        int hi = s.size() + 1;
+        while (lo < hi) {
+            int mid = lo + (hi - lo) / 2;
+
+            if (can_make_valid_substring(s, mid, k)) lo = mid + 1;
+            else hi = mid;
+        }
+
+        // 이진탐색이 종료되면 lo는 최대 길이보다 1 큰 값이 된다.
+        // EX:      hi lo
+        // T  T  T  T  F  F  F  F
+        // 따라서 최대 길이는 lo - 1이 된다
+        return lo - 1;
+    }
+};
diff --git a/merge-two-sorted-lists/flynn.cpp b/merge-two-sorted-lists/flynn.cpp
new file mode 100644
index 000000000..f9dbe7a9f
--- /dev/null
+++ b/merge-two-sorted-lists/flynn.cpp
@@ -0,0 +1,48 @@
+/**
+ * 풀이
+ * - 주어진 두 링크드리스트의 각 node를 비교하며 반환할 새 링크드리스트에 추가해줍니다
+ * 
+ * Big O
+ * - N: 주어진 두 링크드리스트 list1, list2의 노드 개수의 총합
+ * 
+ * - Time complexity: O(N)
+ * - Space complexity: O(1)
+ *   - 반환하는 링크드리스트를 복잡도에 포함시키지 않을 시, 공간복잡도는 N에 상관 없이 일정합니다
+ */
+
+/**
+ * Definition for singly-linked list.
+ * struct ListNode {
+ *     int val;
+ *     ListNode *next;
+ *     ListNode() : val(0), next(nullptr) {}
+ *     ListNode(int x) : val(x), next(nullptr) {}
+ *     ListNode(int x, ListNode *next) : val(x), next(next) {}
+ * };
+ */
+class Solution {
+public:
+    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
+        ListNode* head = new ListNode();
+        ListNode* node = head;
+
+        ListNode* p = list1;
+        ListNode* q = list2;
+
+        while (p != nullptr && q != nullptr) {
+            if (p->val < q->val) {
+                node->next = p;
+                p = p->next;
+            } else {
+                node->next = q;
+                q = q->next;
+            }
+            node = node->next;
+        }
+
+        if (p != nullptr) node->next = p;
+        if (q != nullptr) node->next = q;
+
+        return head->next;
+    }
+};
diff --git a/sum-of-two-integers/flynn.cpp b/sum-of-two-integers/flynn.cpp
new file mode 100644
index 000000000..5d9ddea7e
--- /dev/null
+++ b/sum-of-two-integers/flynn.cpp
@@ -0,0 +1,54 @@
+/**
+ * 풀이
+ * - 두 정수를 한 bit씩 더하는 방식으로 풀이합니다
+ * - 두 정수에 대해 이진 덧셈을 진행할 때, 해당 자리수의 bit 두 개와 carry를 비교하여 새로운 carry와 해당 자리수의 덧셈 결과를 얻을 수 있습니다 -> adder 함수 참고
+ * - 각 비트에 대해 adder 함수를 호출하여 덧셈을 진행합니다
+ * - res의 특정 자리에 덧셈 결과를 넣어주는 것이 까다로웠는데, position이라는 일종의 bitmask를 사용하여 해결할 수 있었습니다
+ * - 저는 Nand2Tetris 라는 책/강의를 보면서 이 전에 bitwise 산술 연산기를 구현한 적이 있었는데, 그 경험이 큰 도움이 되었습니다
+ *   궁금하신 분들께 coursera 강의 링크를 첨부합니다 (무료) (https://www.coursera.org/learn/build-a-computer) (2강에 나옴)
+ * 
+ * Big O
+ * - N: a와 b 중 큰 수의 비트 수 <= 32 (c++ 기준)
+ * 
+ * - Time complexity: O(N <= 32) = O(1)
+ * - Space complexity: O(1)
+ */
+
+class Solution {
+public:
+    // returns {carry, result}
+    // carry와 result를 아래와 같은 bool 연산으로 표현할 수 있다는 사실은
+    // x, y, c에 대하여 벤 다이어그램을 그려보면 쉽게 파악할 수 있습니다
+    pair<bool, bool> adder(bool x, bool y, bool c) {
+        return {(x & y) | (x & c) | (y & c), x ^ y ^ c};
+    }
+
+    int getSum(int a, int b) {
+        bool carry = 0;
+        unsigned int res = 0;
+        unsigned int position = 1;
+
+        // 32 비트 정수 범위 내에서 덧셈을 진행합니다
+        // 32 비트 모두 덧셈을 진행했거나, 더 더할 비트가 없다면 루프를 종료합니다
+        while (position && (a || b || carry)) {
+            bool lsb_a = a & 1;
+            a >>= 1;
+
+            bool lsb_b = b & 1;
+            b >>= 1;
+
+            auto [new_carry, new_res] = adder(lsb_a, lsb_b, carry);
+            
+            carry = new_carry;
+            if (new_res) res |= position;
+
+            // position이 unsigned int (32비트)이므로
+            // bitwise left shift 연산을 32번 수행하면 0이 됨
+            // 1000 0000 0000 0000 0000 0000 0000 0000 => 0000 0000 0000 0000 0000 0000 0000 0000
+            // position이 0이 되면 32비트 모두 덧셈을 완료했다는 뜻이므로 loop를 종료함
+            position <<= 1;
+        }
+
+        return (int) res;
+    }
+};