Hướng dẫn giải của Xâu đối xứng hoàn hảo
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ậpTác giả: , , ,
Editorial for repalind: Xâu đối xứng hoàn hảo
Hiểu bài toán
Cho một xâu $s$ và số nguyên dương $k$. Xâu $t$ được tạo thành bằng cách lặp lại $s$ liên tiếp $k$ lần. Nhiệm vụ là kiểm tra xem $t$ có phải là một xâu đối xứng (palindrome) hay không.
Phân tích:
- Xâu $t$ có độ dài $N = |s| \times k$.
- Xâu $t$ là palindrome nếu và chỉ nếu $t[i] = t[N-1-i]$ với mọi $0 \le i < N$.
- Xâu $t$ có thể được chia thành $k$ phần, mỗi phần là $s$. Ký tự tại vị trí $i$ trong $t$ thuộc về phần thứ $i / |s|$ và có kí tự tương ứng trong $s$ là $s[i \% |s|$.
- Xét điều kiện đối xứng:
- Nếu $|s|$ là số chẵn, $t$ là palindrome nếu và chỉ khi $s$ là palindrome.
- Nếu $|s|$ là số lẻ và $k$ là số chẵn, $t$ là palindrome nếu và chỉ khi $s$ là palindrome.
- Nếu $|s|$ là số lẻ và $k$ là số lẻ, $t$ là palindrome nếu và chỉ khi $s$ là palindrome và "xâu đối xứng lặp" (thuật ngữ trong bài toán này gọi là Perfect Palindrome). Cụ thể, xét $s$ có độ dài $L$ lẻ, trục đối xứng nằm ở ký tự giữa. Để $t$ đối xứng, trục đối xứng của $s$ phải trùng với trục đối xứng của $t$. Điều này dẫn đến yêu cầu thêm về các ký tự của $s$. Ví dụ: $s = "abc"$, $L=3$ (lẻ). Ký tự giữa là 'b'. Để $t = "abcabcabc"$ đối xứng, ta cần $s[0] = s[2]$ ('a' = 'c') và các điều kiện khác. Thực chất, với $s$ độ dài lẻ và $k$ lẻ, $t$ là palindrome khi và chỉ khi $s$ là palindrome.
Tóm lại, lời giải đơn giản nhất là: $t$ là palindrome nếu và chỉ khi $s$ là palindrome.
Các cách tiếp cận
Cách Kiểm tra xâu s có phải palindrome không
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
// Hàm kiểm tra xâu a có phải là palindrome không
bool is_palindrome(char a[]) {
int len = strlen(a);
for (int i = 0; i < len / 2; i++) {
if (a[i] != a[len - i - 1]) {
return false;
}
}
return true;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
char s[5005];
long long k;
scanf("%s %lld", s, &k);
// Logic thực tế:
// Nếu s là palindrome, thì t = s repeated k times luon la palindrome.
// (Phân tích chi tiết trong phần Problem Understanding)
if (is_palindrome(s)) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
- Time Complexity: O(|s|)
- Space Complexity: O(1)
Giải pháp này dựa trên nhận định rằng xâu $t$ được tạo từ $s$ lặp lại $k$ lần là palindrome khi và chỉ khi $s$ là palindrome.
Tại sao chỉ cần kiểm tra $s$?
- Nếu $s$ là palindrome, tính đối xứng của $s$ sẽ được bảo toàn khi lặp lại.
- Nếu $s$ không phải là palindrome, chắc chắn $t$ không thể là palindrome (trừ các trường hợp rất đặc biệt không xảy ra trong bài toán này).
Thao tác:
- Đọc vào $T$.
- Với mỗi test case, đọc $s$ và $k$ (kỳ thực giá trị $k$ không cần thiết cho lời giải này).
- Kiểm tra $s$ có đối xứng không bằng cách so sánh $s[i]$ với $s[len-1-i]$.
Độ phức tạp:
- Thời gian: $O(|s|)$ cho mỗi test case.
- Không gian: $O(1)$ (không dùng thêm bộ nhớ đáng kể).
Cách Code mẫu từ các giải pháp đã nộp (C)
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
bool doixung(char a[]){
int p = strlen(a);
for (int i = 0;i < p / 2; i++){
if (a[i] != a[p - i - 1]) return 0;
}
return 1;
}
int main(){
int n;
scanf("%d",&n);
getchar();
while(n--){
char a[10000];
fgets(a,sizeof(a),stdin);
a[strcspn(a,"\n")] = '\0';
long long k;
scanf("%lld",&k);
getchar();
if (doixung(a)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
- Time Complexity: O(|s|)
- Space Complexity: O(1)
Đây là cách tiếp cận trực quan nhất và cũng là cách đúng đắn nhất cho bài toán này dựa trên các phân tích toán học về tính chất của xâu lặp.
Các bước:
- Nhập số lượng test cases $T$.
- Với mỗi test case:
- Nhập xâu $s$. Lưu ý các solution mẫu có cách nhập khác nhau (
scanfhoặcfgets) nhưng đều hoạt động tốt với điều kiện input. - Nhập số $k$. Mặc dù $k$ có thể lên tới $10^{18}$, nhưng ta không cần dùng nó để tính toán gì nhiều.
- Gọi hàm
doixung(hayis_palindrome) kiểm tra $s$. - In kết quả.
- Nhập xâu $s$. Lưu ý các solution mẫu có cách nhập khác nhau (
Ví dụ:
- $s = "aba", k = 3$. $s$ palindrome -> YES.
- $s = "ccdd", k = 2$. $s$ không palindrome -> NO.
- $s = "freecontest", k = 1$. $s$ không palindrome -> NO.
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên | ||
|---|---|---|---|---|---|
| 1 | O( | s | ) | O(1) | Kiểm tra xâu s có phải palindrome không |
| 2 | O( | s | ) | O(1) | Code mẫu từ các giải pháp đã nộp (C) |
Bài học kinh nghiệm
- Bài toán có vẻ phức tạp do $k$ rất lớn ($10^{18}$), nhưng thực chất việc lặp lại $k$ lần không làm thay đổi tính chất palindrome của xâu gốc $s$. Nếu $s$ là palindrome, $s^k$ (nghĩa là $s$ lặp $k$ lần) cũng là palindrome.
- Lưu ý về cách nhập/xử lý xâu: Vì $s$ chỉ chứa ký tự in thường và số, không có khoảng trắng, có thể dùng
scanf("%s"). Nếu dùngfgetscần chú ý loại bỏ ký tự newline.
Lỗi thường gặp
- Sai lầm khi cố gắng tạo ra xâu $t$ hoặc tính toán phức tạp dựa trên $k$. $k$ có giá trị lên tới $10^{18}$, không thể lưu trữ xâu $t$ (độ dài tối đa $5000 \times 10^{18}$) vào bộ nhớ hay duyệt qua nó.
- Quên xử lý ký tự newline khi trộn lẫn
scanfvàfgets(mặc dù trong bài nàyscanfcho $s$ là đủ). - Lầm tưởng rằng với $k$ chẵn, $t$ luôn palindrome ngay cả khi $s$ không palindrome. Ví dụ $s = "ab", k = 2 \rightarrow t = "abab"$ không palindrome.
Bình luận