짬뽕얼큰하게의 맨땅에 헤딩 :: '분류 전체보기' 카테고리의 글 목록 (19 Page)

Leetcode Problem:

Summary

  • Given an array of integers and an integer k, determine if it is possible to divide the array into exactly n/2 pairs such that the sum of each pair is divisible by k.

Approach

  • This problem can be solved using a hash map to count the remainders of the array elements when divided by k
  • We first calculate the offset as k * k, then for each number in the array, we increment the count of the remainder
  • If the count of 0 is odd, it means that we cannot divide the array into pairs with sum divisible by k
  • Otherwise, we check if the counts of all remainders from 1 to k-1 are equal, and if the count of k/2 is odd when k is even
  • If any of these conditions are not met, we return false
  • Otherwise, we return true.

Complexity

  • O(n) where n is the length of the array

Explain

  • This solution works by first calculating the offset as k * k, then for each number in the array, we increment the count of the remainder
  • If the count of 0 is odd, it means that we cannot divide the array into pairs with sum divisible by k
  • Otherwise, we check if the counts of all remainders from 1 to k-1 are equal, and if the count of k/2 is odd when k is even
  • If any of these conditions are not met, we return false
  • Otherwise, we return true.

Solution Code:


class Solution {
public:
    unordered_map cnt;
    bool canArrange(vector& arr, int k) {
        if (k == 1) return true;
        long long offset = k;
        while(offset < 1e9){
            offset *= k;
        }
        for(long long num : arr){
            cnt[(num + offset) % k]++;
        }
        if(cnt[0] & 1) return false;
        for(int i = 1; i < ((k+1)>>1); i++){
            if(cnt[i] != cnt[k - i]) return false;
        }
        if((k %2) == 0 && ((cnt[k/2] %2) == 1)){
            return false;
        }
        return true;
    }
};

'알고리즘' 카테고리의 다른 글

Make Sum Divisible by P  (0) 2025.02.13
Rank Transform of an Array  (0) 2025.02.13
Design a Stack With Increment Operation  (0) 2025.02.13
All O`one Data Structure  (0) 2025.02.13
Design Circular Deque  (0) 2025.02.13
블로그 이미지

짬뽕얼큰하게

,

Leetcode Problem:

Summary

  • Design a stack that supports increment operations on its elements
  • The CustomStack class is implemented with a maximum size and supports push, pop, and increment operations.

Approach

  • The CustomStack class uses an array to store the stack elements and a separate array to store the lazy values
  • The lazy array is used to store the increment values for each element in the stack
  • The push operation adds an element to the top of the stack, the pop operation removes an element from the top of the stack and updates the lazy values, and the increment operation updates the lazy values for a range of elements in the stack.

Complexity

  • O(1) for push, O(1) for pop, and O(min(k, top)) for increment operations

Explain

  • The CustomStack class is implemented with two arrays, arr and lazy, to store the stack elements and lazy values respectively
  • The top variable is used to keep track of the top element in the stack
  • The push operation adds an element to the top of the stack and updates the lazy value for the top element
  • The pop operation removes an element from the top of the stack and updates the lazy values for the elements below it
  • The increment operation updates the lazy values for a range of elements in the stack
  • The time complexity of the push and pop operations is O(1) because they only involve updating a single element in the stack
  • The time complexity of the increment operation is O(min(k, top)) because it involves updating the lazy values for a range of elements in the stack.

Solution Code:


class CustomStack {
public:
    int stackSize;
    int arr[1001];
    int lazy[1001];
    int top;
    CustomStack(int maxSize) {
        stackSize = maxSize;
        top = 0;
        for(int i = 0 ; i < maxSize; i++){
            lazy[i] = 0;
        }
    }
    
    void push(int x) {
        if (top == stackSize) return;
        arr[top++] = x;
    }
    
    int pop() {
        if (top == 0) return -1;
        top--;
        if (top > 0) lazy[top-1] += lazy[top];
        arr[top] += lazy[top];
        lazy[top] = 0;
        return arr[top];
    }
    
    void increment(int k, int val) {
        if (top == 0) return;
        lazy[min(k-1, top-1)] += val;
    }
};

/**
 * Your CustomStack object will be instantiated and called as such:
 * CustomStack* obj = new CustomStack(maxSize);
 * obj->push(x);
 * int param_2 = obj->pop();
 * obj->increment(k,val);
 */

'알고리즘' 카테고리의 다른 글

Rank Transform of an Array  (0) 2025.02.13
Check If Array Pairs Are Divisible by k  (0) 2025.02.13
All O`one Data Structure  (0) 2025.02.13
Design Circular Deque  (0) 2025.02.13
My Calendar II  (0) 2025.02.13
블로그 이미지

짬뽕얼큰하게

,

Leetcode Problem:

Summary

  • Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum counts.

Approach

  • A Trie data structure is used to store the strings with their counts
  • The Trie is divided into multiple levels based on the count of the strings
  • The maxKey and minKey variables are used to keep track of the maximum and minimum counts respectively.

Complexity

  • O(1) average time complexity

Explain

  • The inc function increments the count of the string key by 1
  • If the key does not exist, it is inserted with count 1
  • The dec function decrements the count of the string key by 1
  • If the count becomes 0, the key is removed from the data structure
  • The getMaxKey and getMinKey functions return one of the keys with the maximum and minimum counts respectively.

Solution Code:


struct _node{
    int cnt;
    int idx;
};
template 
struct trie{
    T1 *nod;
    trie* next[26];
    trie(){
        nod = nullptr;
        for(int i = 0 ; i < 26; i++){
            next[i] = nullptr;
        }
    }
    ~trie(){
        if (nod != nullptr) delete nod;
        for(int i = 0 ; i < 26; i++){
            if(next[i] != nullptr) delete next[i];
            next[i] = nullptr;
        }
    }
};

trie<_node> *root;
_node* getNode(string &s){
    trie<_node>* ptr = root;
    for(char c : s){
        int idx = c - 'a';
        if(ptr->next[idx] == nullptr){
            ptr->next[idx] = new trie<_node>();
        }
        ptr = ptr->next[idx];
    }
    if(ptr->nod == nullptr){
        ptr->nod = new _node();
        ptr->nod->cnt = 0;
        ptr->nod->idx = -1;
    }
    return ptr->nod;
}

template 
struct _vector{
    T* arr;
    int size;
    int capacity;

    _vector(){
        size = 0;
        capacity = 1;
        arr = new T[capacity];
    }

    void push(T &a){
        if(size == capacity){
            capacity*=2;
            T* tmp = new T[capacity];
            for(int i = 0 ; i < size; i++){
                tmp[i] = arr[i];
            }
            delete[] arr;
            arr = tmp;
        }
        arr[size++] = a;
    }

    void del(int idx){
        arr[idx] = arr[--size];
        _node* nod = getNode(arr[idx]);
        nod->idx = idx;
    }
    T operator[](int idx){
        return arr[idx];
    }
};


class AllOne {
private:
    _vector v[50001];
public:
    int maxKey;
    int minKey;
    AllOne() {
        root = new trie<_node>();
        minKey = maxKey = 1;
    }
    ~AllOne() {
        delete root;
    }
    
    void inc(string key) {
        _node* nod = getNode(key);
        if(nod->cnt == minKey && v[nod->cnt].size == 1){
            minKey++;
        }
        if (nod->cnt == 0){
            minKey = 1;
        }
        if (nod->cnt == maxKey){
            maxKey++;
        }
        nod->cnt++;
        if(nod->cnt != 1){
            v[nod->cnt-1].del(nod->idx);
        }
        nod->idx = v[nod->cnt].size;
        v[nod->cnt].push(key);
        
    }
    
    void dec(string key) {
        _node* nod = getNode(key);
        if (nod->cnt == maxKey && v[nod->cnt].size == 1){
            maxKey--;
        }
        if (nod->cnt == minKey){
            minKey--;
        }
        
        v[nod->cnt].del(nod->idx);
        nod->cnt--;
        if(nod->cnt != 0){
            nod->idx = v[nod->cnt].size;
            v[nod->cnt].push(key);
        }
        if(minKey == 0){
            for(int i = minKey+1; i <= maxKey; i++){
                if(v[i].size > 0){
                    minKey = i;
                    break;
                }
            }
        }
    }
    
    string getMaxKey() {
        if (v[maxKey].size > 0) return v[maxKey][0];
        return "";
    }
    
    string getMinKey() {
        if (v[minKey].size > 0) return v[minKey][0];

        return "";
    }
};

/**
 * Your AllOne object will be instantiated and called as such:
 * AllOne* obj = new AllOne();
 * obj->inc(key);
 * obj->dec(key);
 * string param_3 = obj->getMaxKey();
 * string param_4 = obj->getMinKey();
 */

'알고리즘' 카테고리의 다른 글

Check If Array Pairs Are Divisible by k  (0) 2025.02.13
Design a Stack With Increment Operation  (0) 2025.02.13
Design Circular Deque  (0) 2025.02.13
My Calendar II  (0) 2025.02.13
My Calendar I  (0) 2025.02.13
블로그 이미지

짬뽕얼큰하게

,

Design Circular Deque

알고리즘 2025. 2. 13. 08:48

Leetcode Problem:

Summary

  • Design a circular double-ended queue (deque) with methods to insert elements at the front and rear, delete elements from the front and rear, and check if the queue is empty or full.

Approach

  • A circular deque is implemented using a fixed-size array
  • The front and rear indices are used to track the current positions of the front and rear elements
  • The insertFront, insertLast, deleteFront, and deleteLast methods update the front and rear indices accordingly
  • The isEmpty and isFull methods check the front and rear indices to determine if the queue is empty or full.

Complexity

  • O(1) for insertFront, insertLast, deleteFront, deleteLast, getFront, getRear, isEmpty, and isFull operations
  • O(n) for printing the queue, where n is the number of elements in the queue.

Explain

  • The MyCircularDeque class is implemented using a fixed-size array of size k+2
  • The front and rear indices are used to track the current positions of the front and rear elements
  • The insertFront method adds an element at the front of the queue and updates the front index
  • The insertLast method adds an element at the rear of the queue and updates the rear index
  • The deleteFront method removes an element from the front of the queue and updates the front index
  • The deleteLast method removes an element from the rear of the queue and updates the rear index
  • The getFront and getRear methods return the front and rear elements respectively
  • The isEmpty and isFull methods check if the queue is empty or full by comparing the front and rear indices.

Solution Code:


class MyCircularDeque {
public:
    int deque[1001];
    int size;
    int front;
    int rear;
    
    MyCircularDeque(int k) {
        size = k+2;
        front = rear = 0;
    }
    
    bool insertFront(int value) {
        if (isFull()) return false;
        if (isEmpty()){
            rear++;
            rear %= size;
        }
        deque[front--] = value;
        front = (front + size) % size;
        return true;
    }
    
    bool insertLast(int value) {
        if (isFull()) return false;
        if (isEmpty()){
            front = (front-1 + size) % size;
        }
        deque[rear++] = value;
        rear %= size;
        return true;
    }
    
    bool deleteFront() {
        if (isEmpty()) return false;
        front = (front + 1)%size;
        if ((front + 1)%size == rear){
            rear = front;
        }
        
        return true;
    }
    
    bool deleteLast() {
        if (isEmpty()) return false;
        rear = (rear - 1 + size) % size;
        if ((rear - 1 + size)%size == front){
            front = rear;
        }
        return true;
    }
    
    int getFront() {
        if (isEmpty()) return -1;
        int getIdx = (front + 1) % size;
        return deque[getIdx];
    }
    
    int getRear() {
        if (isEmpty()) return -1;
        int getIdx = (rear - 1 + size) % size;
        return deque[getIdx];
    }
    
    bool isEmpty() {
        return front == rear;
    }
    
    bool isFull() {
        return front == (rear + 1) % size;
    }
};

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque* obj = new MyCircularDeque(k);
 * bool param_1 = obj->insertFront(value);
 * bool param_2 = obj->insertLast(value);
 * bool param_3 = obj->deleteFront();
 * bool param_4 = obj->deleteLast();
 * int param_5 = obj->getFront();
 * int param_6 = obj->getRear();
 * bool param_7 = obj->isEmpty();
 * bool param_8 = obj->isFull();
 */

'알고리즘' 카테고리의 다른 글

Design a Stack With Increment Operation  (0) 2025.02.13
All O`one Data Structure  (0) 2025.02.13
My Calendar II  (0) 2025.02.13
My Calendar I  (0) 2025.02.13
Sum of Prefix Scores of Strings  (0) 2025.02.13
블로그 이미지

짬뽕얼큰하게

,

My Calendar II

알고리즘 2025. 2. 13. 08:44

Leetcode Problem:

Summary

  • Implementing a calendar system to avoid triple booking.

Approach

  • This solution uses a data structure to store the events in the calendar
  • It first checks for overlapping events with existing events in the calendar and returns false if a triple booking is found
  • If no overlap is found, it adds the new event to the calendar.

Complexity

  • O(n log n) due to sorting of events in the calendar

Explain

  • The MyCalendarTwo class uses a vector to store the events in the calendar
  • The book method first checks for overlapping events with existing events in the calendar
  • It uses a nested loop to check for overlapping events and returns false if a triple booking is found
  • If no overlap is found, it adds the new event to the calendar
  • The time complexity of this solution is O(n log n) due to sorting of events in the calendar.

Solution Code:


struct _pair{
    int start;
    int end;
};
class MyCalendarTwo {
public:
    vector<_pair> bookList;
    MyCalendarTwo() {
        
    }
    
    bool book(int start, int end) {
        vector<_pair> doubleBookList;
        for(_pair book : bookList){
            if(book.start >= end || book.end <= start){
                continue;
            }
            int s = max(book.start, start);
            int e = min(book.end, end);
            for(_pair db: doubleBookList){
                if(db.start >= e || db.end <= s){
                    continue;
                }
                return false;
            }
            doubleBookList.push_back({s, e});
        }
        bookList.push_back({start, end});
        return true;
    }
};

/**
 * Your MyCalendarTwo object will be instantiated and called as such:
 * MyCalendarTwo* obj = new MyCalendarTwo();
 * bool param_1 = obj->book(start,end);
 */

'알고리즘' 카테고리의 다른 글

All O`one Data Structure  (0) 2025.02.13
Design Circular Deque  (0) 2025.02.13
My Calendar I  (0) 2025.02.13
Sum of Prefix Scores of Strings  (0) 2025.02.13
Max Sum of a Pair With Equal Sum of Digits  (0) 2025.02.12
블로그 이미지

짬뽕얼큰하게

,

My Calendar I

알고리즘 2025. 2. 13. 08:40

Leetcode Problem:

Summary

  • Implementing a program to use as a calendar to avoid double booking.

Approach

  • We use a vector of pairs to store the events in the calendar
  • For each new event, we check if it intersects with any existing events
  • If it does, we return false
  • Otherwise, we add the new event to the calendar.

Complexity

  • O(n) where n is the number of events in the calendar

Explain

  • We define a struct _pair to store the start and end times of each event
  • In the book function, we iterate over each event in the calendar
  • If the end time of the new event is less than or equal to the start time of an existing event, or if the start time of the new event is greater than the end time of an existing event, we continue to the next event
  • If we find an existing event that intersects with the new event, we return false
  • Otherwise, we add the new event to the calendar by pushing it onto the vector of pairs and return true.

Solution Code:


struct _pair{
    int start;
    int end;
};
class MyCalendar {
public:
    vector<_pair> v;
    MyCalendar() {
        
    }
    
    bool book(int start, int end) {
        for(_pair p : v){
            if (end <= p.start || p.end <= start){
                continue;
            }
            return false;
        }
        v.push_back({start, end});
        return true;
    }
};

/**
 * Your MyCalendar object will be instantiated and called as such:
 * MyCalendar* obj = new MyCalendar();
 * bool param_1 = obj->book(start,end);
 */

'알고리즘' 카테고리의 다른 글

Design Circular Deque  (0) 2025.02.13
My Calendar II  (0) 2025.02.13
Sum of Prefix Scores of Strings  (0) 2025.02.13
Max Sum of a Pair With Equal Sum of Digits  (0) 2025.02.12
Find the Length of the Longest Common Prefix  (0) 2025.02.12
블로그 이미지

짬뽕얼큰하게

,

Leetcode Problem:

Summary

  • Given an array of strings, the problem requires finding the sum of scores of every non-empty prefix of each string
  • The score of a string is the number of strings that it is a prefix of.

Approach

  • A Trie data structure is used to store the given words
  • Each node in the Trie represents a character in the word
  • The score of each node is the number of words that it is a prefix of
  • The insert function is used to insert words into the Trie, and the getScore function is used to calculate the score of a given word.

Complexity

  • O(n * m * 26) where n is the number of words and m is the maximum length of a word
  • This is because in the worst case, we need to traverse the Trie for each word and for each character in the word.

Explain

  • The insert function is used to insert words into the Trie
  • It starts from the root node and traverses the Trie based on the characters in the word
  • The score of each node is incremented whenever a word is inserted
  • The getScore function is used to calculate the score of a given word
  • It starts from the root node and traverses the Trie based on the characters in the word, adding the score of each node to the total score.

Solution Code:


struct trie{
    int score;
    trie* next[26];
    bool tail;
    trie(){
        score = 0;
        for(int i = 0 ; i < 26; i++){
            next[i] = nullptr;
        }
        tail = false;
    }
    ~trie(){
        for(int i = 0 ; i < 26; i++){
            if(next[i] != nullptr) delete next[i];
            next[i] = nullptr;
        }
    }
};

void insert(trie* root, string &s, int s_idx, int size){
    if (s_idx == size) {
        root->tail = true;
        return;
    }
    int idx = s[s_idx]-'a';
    if(root->next[idx] == nullptr){
        root->next[idx] = new trie();
    }
    root->next[idx]->score++;
    insert(root->next[idx], s, s_idx+1, size);
}
int getScore(trie* root, string &s, int s_idx, int size){
    if(s_idx == size) return root->score;
    int idx = s[s_idx] - 'a';
    return root->score + getScore(root->next[idx], s, s_idx + 1, size);
}

class Solution {
public:
    vector sumPrefixScores(vector& words) {
        trie t;
        vector ans;
        for(string word : words){
            insert(&t, word, 0, word.size());
        }
        for(string word : words){
            int score = getScore(&t, word, 0, word.size());
            ans.push_back(score);
        }
        return ans;
    }
};

'알고리즘' 카테고리의 다른 글

My Calendar II  (0) 2025.02.13
My Calendar I  (0) 2025.02.13
Max Sum of a Pair With Equal Sum of Digits  (0) 2025.02.12
Find the Length of the Longest Common Prefix  (0) 2025.02.12
Extra Characters in a String  (0) 2025.02.12
블로그 이미지

짬뽕얼큰하게

,

Leetcode Problem:

Summary

  • Find the maximum sum of two numbers in an array where the sum of digits of each number is equal.

Approach

  • The approach used is to calculate the sum of digits of each number and store them in a map
  • Then, for each sum of digits, find the two numbers in the map that can be added together to get the maximum sum
  • If there are multiple pairs, choose the one with the maximum sum.

Complexity

  • O(n log n) due to the sorting of the numbers in the map

Explain

  • The code first calculates the sum of digits of each number using the digit_sum function
  • Then, it stores these sums and their corresponding numbers in a map
  • For each sum of digits, it checks if there are already two numbers in the map that can be added together to get a larger sum
  • If not, it adds the current number to the map and updates the map if the current number is larger than the one already in the map
  • Finally, it iterates over the map to find the maximum sum of two numbers that can be obtained.

Solution Code:


class Solution {
public:
    unordered_map> map;
    int digit_sum(int num){
        int s = 0;
        while(num){
            s += num % 10;
            num /= 10;
        }
        return s;
    }
    int maximumSum(vector& nums) {
        for(int i = 0 ; i < nums.size(); i++){
            int digit = digit_sum(nums[i]);
            if(map[digit].size() < 2){
                map[digit].push_back(nums[i]);
                if(map[digit].size() == 2 && map[digit][0] < map[digit][1]){
                    int t = map[digit][0];
                    map[digit][0] = map[digit][1];
                    map[digit][1] = t;
                }
            } else{
                if(map[digit][0] <= nums[i]){
                    map[digit][1] = map[digit][0];
                    map[digit][0] = nums[i];
                } else{
                    map[digit][1] = max(map[digit][1], nums[i]);
                }
            }
        }
        int ans = -1;
        for(auto iter: map){
            if((iter.second).size() == 2){
                if(ans < (iter.second[0] + iter.second[1])){
                    ans = iter.second[0] + iter.second[1];
                }
            }
        }
        return ans;
    }
};

'알고리즘' 카테고리의 다른 글

My Calendar I  (0) 2025.02.13
Sum of Prefix Scores of Strings  (0) 2025.02.13
Find the Length of the Longest Common Prefix  (0) 2025.02.12
Extra Characters in a String  (0) 2025.02.12
K-th Smallest in Lexicographical Order  (0) 2025.02.12
블로그 이미지

짬뽕얼큰하게

,

Leetcode Problem:

Summary

  • Find the length of the longest common prefix among all pairs of integers (x, y) such that x belongs to arr1 and y belongs to arr2.

Approach

  • Use a set to store the digits of the numbers in arr1 and then for each number in arr2, check if the digits are present in the set
  • If they are, update the answer with the maximum count of digits found so far.

Complexity

  • O(n*m*log(max(arr1[i], arr2[i]))) where n is the size of arr1 and m is the size of arr2

Explain

  • The time complexity of this solution is O(n*m*log(max(arr1[i], arr2[i]))) because for each number in arr1, we are inserting its digits into the set, and for each number in arr2, we are checking if its digits are present in the set
  • The space complexity is O(n*m) because we are storing all the digits in the set.

Solution Code:


class Solution {
public:
    set s;
    int getCount(int a){
        int cnt = 0;
        while(a){
            cnt++;
            a /= 10;
        }
        return cnt;
    }
    int longestCommonPrefix(vector& arr1, vector& arr2) {
        int ans = 0;
        for(int i = 0 ; i < arr1.size(); i++){
            while(arr1[i]){
                s.insert(arr1[i]);
                arr1[i] /= 10;
            }
        }
        for(int i = 0 ; i < arr2.size(); i++){
            while(arr2[i]){
                if (s.find(arr2[i]) != s.end()){
                    ans = max(ans, getCount(arr2[i]));
                    break;
                }
                arr2[i] /= 10;
            }
        }
        return ans;
    }
};

'알고리즘' 카테고리의 다른 글

Sum of Prefix Scores of Strings  (0) 2025.02.13
Max Sum of a Pair With Equal Sum of Digits  (0) 2025.02.12
Extra Characters in a String  (0) 2025.02.12
K-th Smallest in Lexicographical Order  (0) 2025.02.12
Largest Number  (0) 2025.02.12
블로그 이미지

짬뽕얼큰하게

,

Leetcode Problem:

Summary

  • Given a string s and a dictionary of words, find the minimum number of extra characters left over if the string is broken into non-overlapping substrings present in the dictionary.

Approach

  • Dynamic programming approach, where a 1D array dp is used to store the minimum number of extra characters for each prefix of the string
  • The function go recursively tries to match the current prefix with each word in the dictionary, and updates the dp array accordingly.

Complexity

  • O(n*m*len(word)) where n is the length of the string, m is the number of words in the dictionary, and len(word) is the length of each word in the dictionary.

Explain

  • The function go takes a string s, a dictionary of words dict, and an integer i as input
  • It first checks if the current prefix of the string has been processed before (i.e., dp[i] is not -1)
  • If not, it sets dp[i] to 1 (for the extra character) and recursively calls itself for the next character
  • Then it tries to match the current prefix with each word in the dictionary, and updates dp[i] with the minimum of its current value and the result of the recursive call for the next character plus the length of the current word
  • The function minExtraChar initializes the dp array and calls the function go with the initial prefix as an empty string.

Solution Code:


class Solution {
public:
    int dp[51];
    int go(string & s, vector& dict, int i){
        if(i == s.size()) return 0;

        if (dp[i] == -1){
            dp[i] = 1 + go(s, dict, i+1);
            for(auto &w: dict){
                if(s.compare(i, w.size(), w) == 0){
                    dp[i] = min(dp[i], go(s, dict, i+w.size()));
                }
            }
        }
        return dp[i];

    }
    int minExtraChar(string s, vector& dictionary) {
        memset(dp, -1, sizeof(dp));
        return go(s, dictionary, 0);
    }
};

'알고리즘' 카테고리의 다른 글

Max Sum of a Pair With Equal Sum of Digits  (0) 2025.02.12
Find the Length of the Longest Common Prefix  (0) 2025.02.12
K-th Smallest in Lexicographical Order  (0) 2025.02.12
Largest Number  (0) 2025.02.12
XOR Queries of a Subarray  (0) 2025.02.12
블로그 이미지

짬뽕얼큰하게

,