Hướng dẫn giải của Tìm kiếm nhị phân 1


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, Viet12, kiethaduong1, nquang2909

Editorial for search1: Tìm kiếm nhị phân 1

Hiểu bài toán

Cho hai dãy số A (có độ dài n) và B (có độ dài m). Dãy A đã được sắp xếp không giảm. Nhiệm vụ là với mỗi phần tử trong dãy B, kiểm tra xem nó có xuất hiện trong dãy A hay không. Xuất ra một xâu nhị phân độ dài m, trong đó ký tự thứ i là '1' nếu b[i] có trong A, ngược lại là '0'. Giới hạn n, m lên tới 100,000.

Các cách tiếp cận

Cách Brute Force (Duyệt tuần tự)
#include <stdio.h>

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    int a[n], b[m];
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    for(int i = 0; i < m; i++) scanf("%d", &b[i]);

    for(int i = 0; i < m; i++) {
        int found = 0;
        for(int j = 0; j < n; j++) {
            if(a[j] == b[i]) {
                found = 1;
                break;
            }
        }
        printf("%d", found);
    }
    return 0;
}
  • Time Complexity: O(n * m) ~ 10^10 operations (Quá chậm)
  • Space Complexity: O(n + m)

Với mỗi phần tử trong mảng B, ta duyệt qua toàn bộ mảng A để tìm kiếm. Do cả n và m đều có thể lên tới 10^5, độ phức tạp O(n*m) sẽ dẫn đến thời gian chạy quá giới hạn (Time Limit Exceeded).

Cách Binary Search (Tìm kiếm nhị phân)
#include <stdio.h>
#include <stdlib.h>

int binarySearch(int a[], int n, int x) {
    int l = 0, r = n - 1;
    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (a[mid] == x) return 1;
        if (a[mid] < x) l = mid + 1;
        else r = mid - 1;
    }
    return 0;
}

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    int *a = (int*)malloc(n * sizeof(int));
    int *b = (int*)malloc(m * sizeof(int));

    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    for(int i = 0; i < m; i++) scanf("%d", &b[i]);

    for(int i = 0; i < m; i++) {
        printf("%d", binarySearch(a, n, b[i]));
    }

    free(a); free(b);
    return 0;
}
  • Time Complexity: O(m * log n)
  • Space Complexity: O(n + m)

Tận dụng việc mảng A đã được sắp xếp. Với mỗi phần tử trong B, ta thực hiện tìm kiếm nhị phân trong A. Độ phức tạp là O(m * log n), với n, m <= 10^5 thì khoảng 1.7 triệu thao tác, hoàn toàn chấp nhận được. Đây là phương pháp tối ưu nhất cho yêu cầu bài toán.

Cách Hash Map (Đếm tần suất)
#include <stdio.h>
#include <stdlib.h>

// Giả lập Hash Map bằng mảng quy hoạch động hoặc cây (đối với số lớn)
// Ở đây dùng cây hoặc map chuẩn của C++ là tốt nhất.
// Code minh họa logic:

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    // Sử dụng mảng đánh dấu (nếu số nhỏ) hoặc hash map (nếu số lớn)
    // Do |a_i| <= 10^9, ta cần dùng map hoặc set.
    // Dưới đây là code minh họa logic dùng Set/Cây (thay vì hash map trực tiếp trong C)
    // Logic: Duyệt mảng A, chèn vào Cây/Trie/Hash Map. Sau đó kiểm tra B.
    // Độ phức tạp O((n+m) log n) hoặc O(n+m) nếu dùng hash tốt.
    // Code C++ tham khảo:
    /*
    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int n, m; cin >> n >> m;
        unordered_set<int> s;
        for(int i=0; i<n; i++) { int x; cin >> x; s.insert(x); }
        for(int i=0; i<m; i++) { int x; cin >> x; cout << (s.count(x) ? "1" : "0"); }
    }
    */
    return 0;
}
  • Time Complexity: O(n + m) (trung bình) hoặc O((n+m) log n)
  • Space Complexity: O(n)

Sử dụng Hash Map (hoặc Set) để lưu các phần tử của mảng A. Sau đó, với mỗi phần tử trong B, ta kiểm tra sự tồn tại trong Hash Map với thời gian O(1) trung bình. Phương pháp này cũng hiệu quả nhưng thường cần nhiều bộ nhớ hơn và phức tạp hơn trong lập trình C thuần so với Binary Search.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(n * m) ~ 10^10 operations (Quá chậm) O(n + m) Brute Force (Duyệt tuần tự)
2 O(m * log n) O(n + m) Binary Search (Tìm kiếm nhị phân)
3 O(n + m) (trung bình) hoặc O((n+m) log n) O(n) Hash Map (Đếm tần suất)

Bài học kinh nghiệm

  • Mảng A đã được sắp xếp là manh mối quan trọng nhất, cho phép áp dụng tìm kiếm nhị phân.
  • Với bài toán tìm kiếm nhiều lần (m truy vấn) trên một mảng tĩnh, tìm kiếm nhị phân là lựa chọn tối ưu về cân bằng giữa thời gian và bộ nhớ.

Lỗi thường gặp

  • Lỗi chia đôi tràn số khi tính mid: Nên dùng mid = left + (right - left) / 2 thay vì (left + right) / 2 để tránh tràn số int.
  • Quên kiểm tra biên hoặc vòng lặp vô hạn trong binary search nếu điều kiện left <= right không đúng.

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.