Hướng dẫn cho Chuỗi hạt nhiều màu


Chỉ sử dụng khi thực sự cần thiết như một cách tôn trọng tác giả và người viết hướng dẫn này.

Chép code từ bài hướng dẫn để nộp bài là hành vi có thể dẫn đến khóa tài khoản.

Authors: letangphuquy

Tóm tắt: đây là bài toán tìm xâu con (substring) trên xâu vòng tròn.

Lưu ý: ở đây tôi dùng từ "substring" có nghĩa là xâu con không liên tiếp chứ không phải là xâu liên tiếp như bạn thường thấy.

Vì mình muốn phát biểu của đề bài dễ hiểu nên đã mô tả thuật toán cày trâu của subtask1, các bạn chỉ việc dịch từ lời nói thành code.

Subtask 1: \(n \le 100\)

Ta sẽ chọn từng vị trí bắt đầu rồi tiến hành tìm substring như đề bài bảo.

Độ phức tạp \(O(n^2)\)

Subtask 2: \(m \le 3\)

Với \(m = 2\), ta thấy chỉ cần 2 kí tự có xuất hiện trong xâu thì ta sẽ tìm được.
Cụ thể, giả sử \(P =\) ab, nếu trong xâu \(S\):

  • a xuất hiện trước b \(\rightarrow\) in ra \(1\)
  • a xuất hiện sau b \(\rightarrow\) in ra vị trí của b.

Với \(m = 3\), giả sử $P = $ abc. Kí hiệu \(xa, xb, xc\) là vị trí xuất hiện các kí tự a, b, c. Nếu \(S\) có chứa \(P\) thì:

  • \(xa < xb < xc\) hoặc
  • \(xc < xa < xb\) hoặc
  • \(xb < xc < xa\)

Nhận thấy những điều kiện trên sẽ tương tự với việc tìm các xâu abc, cab, bca trong xâu \(S\).

Công việc của ta chỉ là tìm substring trên xâu bình thường (không vòng tròn) nhưng sẽ xét 3 trường hợp của \(P\). Đáp án sẽ là \(xa\).

Độ phức tạp \(O(n)\)

Subtask 3: \(m \le n\)

Tổng quát hóa ý tưởng của subtask 2: thay vì đẩy xâu \(S\) theo vòng tròn, ta sẽ đẩy xâu \(P\) theo vòng tròn và tiến hành tìm substring.

Trong quá trình tìm, phải ghi nhớ chỉ số của kí tự đầu tiên của \(P\) là bao nhiêu? Đó là vì khi ta đẩy xâu \(P\) thì các kí tự cũng bị thay đổi vị trí.
Ví dụ: Sau khi đẩy về "bên phải" một lần thì kí tự đầu tiên trong xâu \(P\) ban đầu là kí tự thứ hai trong xâu \(P\) hiện tại (sau khi đẩy).

Trong lúc tìm \(P\) trong \(S\), nhớ ghi nhận lại vị trí mà ta tìm thấy kí tự đầu tiên đó - nó sẽ là đáp án bài toán (vì \(m \le n\) nên tìm từ kí tự đầu tiên của \(P\) thì chắc chắn sẽ tìm thấy hết mọi kí tự của \(P\)).

Độ phức tạp \(O(nm)\)

Bonus:

  • Có một điều kiện giúp thuật trâu chạy được trong \(0,1s\): chỉ bắt đầu tại những vị trí cùng màu với \(P[0]\)
  • Vì sự cố nên bộ test trong contest rất dễ (chỉ cần in ra 1 hoặc cày trâu vẫn \(AC\))
  • Cày trâu "ngây thơ" sẽ qua được trong \(1s\) nhưng không qua được \(0,1s\)
  • Bộ test chưa có test giết "trâu khôn", sẽ add (soon^TM)


Bình luận


  • 0
    huyhau6a2    8:30 p.m. 12 Tháng 9, 2022 đã chỉnh sửa

    Mình xin phép trình bày 1 cách làm khác của bài toán này như sau:

    • Ta sẽ tạo mảng \(F[26][n]\). \(F[i][j]\) biểu thị khoảng cách từ vị trí \(j\) đến vị trí gần nhất xuất hiện ký tự \(i\)(ở đây \(1='a',2='b',3='c',...,26='z'\)) từ vị trí \(j+1\), trường hợp không xuất hiện đặt \(F[i][j]=n+1\)
    • Khi duyệt, ta sẽ làm cày trâu tương tự như đề bài đã bảo, nhưng ta sẽ dựa vào \(F[i][j]\) để tìm vị trí ký tự trong \(O(1)\)

    Với cách làm này sẽ có độ phức tạp \(O(n.m)\)

    Độ phức tạp tối đa là \(10^6\), mình nghĩ c++ có thể ac trong 0,1s, nhưng với py chắc cần nâng lên 1s!

    1 phản hồi