Hướng dẫn giải của Mã hóa mật khẩu
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 encrypt: Mã hóa mật khẩu
Hiểu bài toán
Bài toán yêu cầu giải mã một mật khẩu đã bị mã hóa. Mật khẩu gốc bao gồm một chuỗi các ký tự chữ cái (tiền tố) và một số nguyên dương ở cuối. Quá trình mã hóa thực hiện bằng cách chèn các chữ số (từ 0 đến 9) vào bất kỳ vị trí nào trong phần chuỗi chữ cái (kể cả đầu và cuối) sao cho tổng của tất cả các chữ số được chèn bằng với số ở cuối mật khẩu gốc.
Ví dụ: Mật khẩu gốc 'Abcd12' được mã hóa thành 'A1b23c4d2'.
- Phần chữ cái gốc: 'Abcd'.
- Số cuối gốc: 12.
- Các chữ số chèn: 1, 2, 3, 4, 2. Tổng = 12.
Đầu vào là một xâu đã mã hóa, chỉ chứa chữ cái Latinh và chữ số. Đầu ra cần khôi phục lại mật khẩu gốc (chuỗi chữ cái + số cuối).
Các cách tiếp cận
Cách Trích xuất và Tách từ (String Extraction & Tokenization)
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main(){
char a[100005];
scanf(" %s",a);
char b[] = "0123456789";
char c[100005] = {'\0'};
char d[100005] = "";
strcpy(c,a);
char *token = strtok(c,b);
while(token != NULL){
strcat(d,token);
token = strtok(NULL,b);
}
int p = strlen(a);
long long sum = 0;
for (int i = 0;i < p; i++){
if (isdigit(a[i])) sum += (a[i] - '0');
}
printf("%s%lld",d,sum);
return 0;
}
- Time Complexity: O(N)
- Space Complexity: O(N)
Phương pháp này sử dụng hàm strtok để tách chuỗi.
- Chuỗi
bchứa tất cả các ký tự phân tách (các chữ số từ 0 đến 9). strtokđược gọi trên bản sao của chuỗi đầu vào để tìm các token là các chuỗi con chỉ chứa chữ cái, ngăn cách bởi các chữ số.- Các token này được nối lại vào chuỗi
d. - Vòng lặp riêng tính tổng các chữ số trong chuỗi gốc.
- Kết quả là chuỗi
dnối với tổng các chữ số. Cách này tận dụng thư viện sẵn có nhưngstrtokcó thể chậm và tiêu tốn bộ nhớ cho việc tạo bản sao và thao tác chuỗi.
Cách Duyệt đơn giản (Single Pass Iteration)
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
int main() {
char str[100005];
char cpy[100005];
scanf("%s",str);
int m = strlen(str);
int k = 0,cnt = 0;
for (int i = 0;i < m;i++){
if ((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z')){
cpy[k++] = str[i];
}
else if (str[i] >= '0' && str[i] <= '9'){
cnt += str[i] - '0';
}
}
printf("%s%d",cpy,cnt);
}
- Time Complexity: O(N)
- Space Complexity: O(N)
Đây là phương pháp trực quan và hiệu quả nhất.
- Khởi tạo một mảng ký tự
cpyđể lưu phần chữ cái và một biến số nguyêncntđể lưu tổng các chữ số. - Duyệt qua từng ký tự của chuỗi đầu vào.
- Nếu ký tự là chữ cái, thêm vào mảng
cpy. - Nếu ký tự là chữ số, cộng giá trị số đó vào
cnt. - Sau vòng lặp, in ra chuỗi
cpyvà giá trịcnt. Phương pháp này chỉ cần duyệt qua chuỗi một lần duy nhất, tối ưu cả về thời gian và bộ nhớ so với cách dùngstrtok.
Cách Trích xuất thủ công (Manual Extraction)
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main() {
char s[100005];
scanf("%s", s);
int len = strlen(s);
char letters[100005];
int letter_idx = 0;
int digit_sum = 0;
for(int i = 0; i < len; i++) {
if(isalpha(s[i])) {
letters[letter_idx++] = s[i];
} else {
digit_sum += s[i] - '0';
}
}
letters[letter_idx] = '\0';
printf("%s%d", letters, digit_sum);
return 0;
}
- Time Complexity: O(N)
- Space Complexity: O(N)
Đây là biến thể của phương pháp duyệt đơn giản, sử dụng hàm isalpha và isdigit để kiểm tra kiểu ký tự thay vì so sánh trực tiếp mã ASCII. Logic hoạt động giống hệt phương pháp thứ 2: tách riêng các chữ cái và tính tổng các chữ số, sau đó ghép lại.
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|---|---|---|---|
| 1 | O(N) | O(N) | Trích xuất và Tách từ (String Extraction & Tokenization) |
| 2 | O(N) | O(N) | Duyệt đơn giản (Single Pass Iteration) |
| 3 | O(N) | O(N) | Trích xuất thủ công (Manual Extraction) |
Bài học kinh nghiệm
- Thứ tự các chữ cái trong phần mã hóa không thay đổi so với mật khẩu gốc, chỉ có các chữ số được chèn vào.
- Tổng các chữ số chèn vào bằng chính giá trị của số cuối trong mật khẩu gốc.
- Việc giải mã không cần quan tâm đến vị trí chèn các chữ số, chỉ cần tách hết các chữ số ra và tính tổng.
Lỗi thường gặp
- Sử dụng hàm
getscó thể gây tràn bộ nhớ đệm nếu không kiểm soát độ dài, nên dùngscanfhoặcfgets. - Quên khai báo mảng đủ lớn để chứa chuỗi đầu vào (cần lên tới 10^5 ký tự).
- Lỗi kiểu dữ liệu khi in tổng số nếu số đó quá lớn, nhưng theo đề bài số cuối tối đa 6 chữ số và tổng các chữ số chèn tối đa 9*10^5 nên
intlà đủ, nhưnglong longlà an toàn hơn.
Bình luận