โจทย์
สร้างฟังก์ชันเปรียบเทียบสตริง (strcmp) ที่ ไม่สนใจพิมพ์เล็ก/พิมพ์ใหญ่ (case-insensitive)
คืนค่า: -1 หาก str1 < str2, 0 หาก str1 == str2, 1 หาก str1 > str2
ตัวอย่าง
Input: "Hello", "hello"
Output: 0 (เหมือนกันเมื่อไม่สนใจ case)
อัลกอริทึม
- วนลูปเปรียบเทียบตัวอักษรทีละคู่
- แปลงทั้งคู่ให้เป็นตัวพิมพ์เล็ก (หรือใหญ่) ก่อนเปรียบเทียบ
- หากต่างกัน คืนค่า -1 หรือ 1 ตามลำดับ ASCII
- หากจบสตริงพร้อมกัน คืนค่า 0
โค้ดตัวอย่าง
#include <iostream>
#include <cctype> // for tolower
using namespace std;
int strcmpIgnoreCase(const char* str1, const char* str2) {
while (*str1 && *str2) {
char c1 = tolower(static_cast<unsigned char>(*str1));
char c2 = tolower(static_cast<unsigned char>(*str2));
if (c1 != c2) {
return (c1 < c2) ? -1 : 1;
}
str1++;
str2++;
}
// หนึ่งสตริงจบ ก่อนอีกตัว
if (*str1 == '\0' && *str2 == '\0') return 0;
return (*str1 == '\0') ? -1 : 1;
}
// Overload สำหรับ std::string
int strcmpIgnoreCase(const string& s1, const string& s2) {
size_t n = min(s1.size(), s2.size());
for (size_t i = 0; i < n; i++) {
char c1 = tolower(static_cast<unsigned char>(s1[i]));
char c2 = tolower(static_cast<unsigned char>(s2[i]));
if (c1 != c2) return (c1 < c2) ? -1 : 1;
}
if (s1.size() == s2.size()) return 0;
return (s1.size() < s2.size()) ? -1 : 1;
}
int main() {
cout << "Case-insensitive strcmp results:\n";
cout << "HELLO vs hello: " << strcmpIgnoreCase("HELLO", "hello") << endl;
cout << "empty vs Hello: " << strcmpIgnoreCase("", "Hello") << endl;
cout << "Tutorials vs Pint: " << strcmpIgnoreCase("Tutorials", "Pint") << endl;
cout << "afdadsa vs rewf: " << strcmpIgnoreCase("afdadsa", "rewf") << endl;
cout << "tutorialspoint vs TUTORIALSpoint: " << strcmpIgnoreCase("tutorialspoint", "TUTORIALSpoint") << endl;
return 0;
}
ผลลัพธ์
Case-insensitive strcmp results: HELLO vs hello: 0 empty vs Hello: -1 Tutorials vs Pint: 1 afdadsa vs rewf: -1 tutorialspoint vs TUTORIALSpoint: 0
เทคนิค Bitwise Trick (ASCII Only)
ใน ASCII: ตัวพิมพ์ใหญ่ (A-Z: 65-90) และตัวพิมพ์เล็ก (a-z: 97-122) ต่างกันที่ bit 5 (ค่า 32)
// 'A' (01000001) ^ 32 = 'a' (01100001)
// 'a' (01100001) ^ 32 = 'A' (01000001)
char toLowerASCII(char c) {
return (c >= 'A' && c <= 'Z') ? (c | 32) : c; // set bit 5
}
// หรือ
char toUpperASCII(char c) {
return (c >= 'a' && c <= 'z') ? (c & ~32) : c; // clear bit 5
}
C++ Standard Library Alternatives
#include <algorithm>
#include <cctype>
#include <string>
// 1. strcasecmp (POSIX, not standard C++)
// int result = strcasecmp(str1, str2);
// 2. Custom comparator with std::lexicographical_compare
bool caseInsensitiveLess(const string& a, const string& b) {
return lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
[](char c1, char c2) { return tolower(c1) < tolower(c2); });
}
// 3. Boost (if available)
// boost::algorithm::ilexicographical_compare
Complexity
- Time: O(min(len1, len2))
- Space: O(1)
สรุป
Case-insensitive string comparison ต้องใช้ tolower() หรือ toupper() จาก <cctype> พร้อม cast เป็น unsigned char เพื่อหลีกเลี่ยง undefined behavior กับค่าลบ บน ASCII สามารถใช้ bitwise trick (| 32) ได้ แต่ไม่ portable สำหรับ EBCDIC หรือ Unicode