[0]zero-index clytie0x/zero-index

6.6 แบบฝึกหัด

เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น

ข้อ 1 ป้ายตัวพิมพ์ใหญ่

ระดับง่าย เวลาไม่เกิน 1 วินาที

ครูให้น้องเขียนชื่อกิจกรรมลงป้ายหน้าห้อง ป้ายต้องใช้ตัวพิมพ์ใหญ่ทั้งหมดจะได้อ่านจากไกลๆ ได้ ให้รับชื่อกิจกรรม S ที่เป็นคำเดียว แล้วพิมพ์ออกมาเป็นตัวพิมพ์ใหญ่ทั้งคำ

ข้อมูลเข้า

คำ S หนึ่งคำ ประกอบด้วยตัวอักษรภาษาอังกฤษพิมพ์ใหญ่หรือพิมพ์เล็กเท่านั้น ไม่มีช่องว่าง ความยาวของ S อยู่ระหว่าง 1 ถึง 100 ตัว

ข้อมูลออก

S ที่เปลี่ยนเป็นตัวพิมพ์ใหญ่ทุกตัว

Sports
SPORTS
ดูคำใบ้

วนทีละช่องจนเจอ '\0' ถ้าช่องนั้นเป็นตัวพิมพ์เล็ก ให้เปลี่ยนด้วย c - 'a' + 'A' หรือ toupper จากหน้า 6.1 ตัวที่เป็นตัวพิมพ์ใหญ่อยู่แล้วต้องไม่เปลี่ยน อาเรย์ต้องมีอย่างน้อย 101 ช่อง

ดูเฉลย
#include <stdio.h>

int main()
{
    char word[105];
    scanf("%s", word);
    for (int i = 0; word[i] != '\0'; i++) {
        if (word[i] >= 'a' && word[i] <= 'z') {
            word[i] = word[i] - 'a' + 'A';
        }
    }
    printf("%s\n", word);
    return 0;
}

ข้อ 2 ผลบวกเลขโดดของเลขยักษ์

ระดับง่าย เวลาไม่เกิน 1 วินาที

ครูเขียนตัวเลขยาวมากเต็มกระดาน แล้วถามว่าถ้าเอาเลขโดดทุกหลักมาบวกกันจะได้เท่าไร เช่น 2569 ได้ 2 + 5 + 6 + 9 = 22 ให้เขียนโปรแกรมตอบคำถามนี้

ข้อมูลเข้า

จำนวนเต็มบวก N หนึ่งตัว ไม่มีเลข 0 นำหน้า N มีจำนวนหลักตั้งแต่ 1 ถึง 1000 หลัก

ข้อมูลออก

ผลบวกของเลขโดดทุกหลักของ N

2569
22
ดูคำใบ้

เลข 1000 หลักใหญ่เกินกว่า long long ที่เก็บได้แค่ 19 หลัก ให้รับเป็นสตริงด้วย %s แล้วแปลงทีละตัวด้วย c - '0' ผลบวกมากสุดคือ 9 × 1000 ซึ่ง int เก็บได้สบาย

ดูเฉลย
#include <stdio.h>

int main()
{
    char number[1005];
    scanf("%s", number);
    int sum = 0;
    for (int i = 0; number[i] != '\0'; i++) {
        sum += number[i] - '0';
    }
    printf("%d\n", sum);
    return 0;
}

ข้อ 3 นับชนิดตัวอักษร

ระดับง่าย เวลาไม่เกิน 1 วินาที

ครูพิมพ์ประกาศหนึ่งบรรทัดก่อนส่งเข้ากลุ่มของห้อง แล้วอยากรู้ว่าในประกาศนั้นมีตัวพิมพ์ใหญ่ ตัวพิมพ์เล็ก ตัวเลข และช่องว่าง อย่างละกี่ตัว

ข้อมูลเข้า

ข้อความหนึ่งบรรทัด ยาว 1 ถึง 1000 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข ช่องว่าง และเครื่องหมายวรรคตอน ข้อความอาจขึ้นต้นหรือลงท้ายด้วยช่องว่าง

ข้อมูลออก

บรรทัดเดียว มีจำนวนเต็มสี่ตัวคั่นด้วยช่องว่าง คือจำนวนตัวพิมพ์ใหญ่ จำนวนตัวพิมพ์เล็ก จำนวนตัวเลข และจำนวนช่องว่าง ตัวขึ้นบรรทัดใหม่ท้ายข้อความไม่นับเป็นช่องว่าง

Bus 8 leaves at 7:45 AM.
3 10 4 5
ดูคำใบ้

ข้อความมีช่องว่าง %s จะได้แค่คำแรก ต้องรับด้วย fgets ตามหน้า 6.3 ระวังตัว '\n' ที่ fgets ติดมา ถ้านับช่องว่างด้วย isspace จะนับ '\n' เกินมาหนึ่งตัว เทียบกับ ' ' ตรงๆ ปลอดภัยกว่า

ดูเฉลย
#include <stdio.h>

int main()
{
    char line[1005];
    fgets(line, sizeof line, stdin);
    int upper = 0, lower = 0, digits = 0, spaces = 0;
    for (int i = 0; line[i] != '\0'; i++) {
        char c = line[i];
        if (c >= 'A' && c <= 'Z') {
            upper++;
        } else if (c >= 'a' && c <= 'z') {
            lower++;
        } else if (c >= '0' && c <= '9') {
            digits++;
        } else if (c == ' ') {
            spaces++;
        }
    }
    printf("%d %d %d %d\n", upper, lower, digits, spaces);
    return 0;
}

ข้อ 4 หัวแถวท้ายแถว

ระดับง่าย เวลาไม่เกิน 1 วินาที

ครูจะเรียงใบรายชื่อนักเรียนตามลำดับในพจนานุกรม แบบเดียวกับที่ strcmp ใช้ ก่อนเรียง ครูอยากรู้ว่าชื่อไหนจะอยู่บนสุด และชื่อไหนจะอยู่ล่างสุด

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N คือจำนวนนักเรียน โดยที่ 1 ≤ N ≤ 1000

อีก N บรรทัด บรรทัดละหนึ่งชื่อ ชื่อประกอบด้วยตัวพิมพ์เล็กภาษาอังกฤษเท่านั้น ยาว 1 ถึง 20 ตัว ชื่ออาจซ้ำกันได้

ข้อมูลออก

บรรทัดแรกพิมพ์ชื่อที่มาก่อนทุกชื่อ บรรทัดที่สองพิมพ์ชื่อที่มาหลังทุกชื่อ

5
somchai
malee
anan
wichai
kanokwan
anan
wichai
ดูคำใบ้

เหมือนหาค่ามากสุดและน้อยสุดในบทที่ 5 แต่เทียบด้วย strcmp และเก็บด้วย strcpy เพราะใช้ < กับ = กับสตริงไม่ได้ ให้ค่าเริ่มต้นของทั้งสองตัวเป็นชื่อแรกที่อ่านได้

ดูเฉลย
#include <stdio.h>
#include <string.h>

int main()
{
    int n;
    char name[25], first[25], last[25];
    scanf("%d", &n);
    scanf("%s", name);
    strcpy(first, name);
    strcpy(last, name);
    for (int i = 1; i < n; i++) {
        scanf("%s", name);
        if (strcmp(name, first) < 0) {
            strcpy(first, name);
        }
        if (strcmp(name, last) > 0) {
            strcpy(last, name);
        }
    }
    printf("%s\n%s\n", first, last);
    return 0;
}

ข้อ 5 ประโยคกระจก

ระดับกลาง เวลาไม่เกิน 1 วินาที

ประโยคบางประโยคอ่านจากหลังมาหน้าได้เหมือนเดิม ถ้าไม่สนใจช่องว่าง เครื่องหมาย ตัวเลข และตัวพิมพ์ใหญ่เล็ก เช่น Was it a car or a cat I saw? เหลือเฉพาะตัวอักษรเป็น wasitacaroracatisaw ซึ่งกลับหลังแล้วเหมือนเดิม ให้ตรวจว่าประโยคที่ได้รับเป็นประโยคแบบนี้หรือไม่

ข้อมูลเข้า

ข้อความหนึ่งบรรทัด ยาว 1 ถึง 100000 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข ช่องว่าง และเครื่องหมายวรรคตอน มีตัวอักษรภาษาอังกฤษอย่างน้อยหนึ่งตัว

ข้อมูลออก

พิมพ์ YES ถ้าตัวอักษรภาษาอังกฤษในข้อความ เมื่อไม่สนตัวพิมพ์ใหญ่เล็ก อ่านจากหลังมาหน้าได้เหมือนจากหน้าไปหลัง ไม่อย่างนั้นพิมพ์ NO ตัวอื่นที่ไม่ใช่ตัวอักษรให้ข้ามไปทั้งหมด

Was it a car or a cat I saw?
YES
Hello, World
NO
ดูคำใบ้

ทำสองขั้น ขั้นแรกเดินทั้งบรรทัด เก็บเฉพาะตัวอักษรที่ผ่าน isalpha แปลงเป็นตัวพิมพ์เล็กด้วย tolower ลงอีกอาเรย์หนึ่ง ขั้นที่สองเช็ก palindrome กับอาเรย์ใหม่นั้นแบบในหน้า 6.5 บรรทัดยาวได้หนึ่งแสนตัว ประกาศอาเรย์ทั้งสองไว้นอก main

ดูเฉลย
#include <stdio.h>
#include <ctype.h>

char line[100005];
char letters[100005];

int main()
{
    fgets(line, sizeof line, stdin);
    int length = 0;
    for (int i = 0; line[i] != '\0'; i++) {
        if (isalpha(line[i])) {
            letters[length] = tolower(line[i]);
            length++;
        }
    }
    int is_palindrome = 1;
    for (int i = 0; i < length / 2; i++) {
        if (letters[i] != letters[length - 1 - i]) {
            is_palindrome = 0;
        }
    }
    if (is_palindrome) {
        printf("YES\n");
    } else {
        printf("NO\n");
    }
    return 0;
}

ข้อ 6 ตัวอักษรยอดนิยม

ระดับกลาง เวลาไม่เกิน 1 วินาที

ชมรมภาษาอังกฤษอยากรู้ว่าในข้อความหนึ่งบรรทัด ตัวอักษรตัวไหนถูกใช้บ่อยที่สุด โดยถือว่าตัวพิมพ์ใหญ่กับตัวพิมพ์เล็กเป็นตัวเดียวกัน เช่น B กับ b และไม่นับตัวที่ไม่ใช่ตัวอักษร

ข้อมูลเข้า

ข้อความหนึ่งบรรทัด ยาว 1 ถึง 100000 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข ช่องว่าง และเครื่องหมายวรรคตอน มีตัวอักษรภาษาอังกฤษอย่างน้อยหนึ่งตัว

ข้อมูลออก

บรรทัดเดียว พิมพ์ตัวอักษรที่พบบ่อยที่สุดเป็นตัวพิมพ์เล็ก ตามด้วยช่องว่างหนึ่งช่อง แล้วตามด้วยจำนวนครั้งที่พบ ถ้ามีหลายตัวที่พบบ่อยเท่ากัน ให้ตอบตัวที่มาก่อนในลำดับ a ถึง z

Banana Bread
a 4
abc, cba!
a 2
ดูคำใบ้

นับด้วย int count[26] แบบในหน้า 6.5 ต้อง tolower ก่อนเสมอ ไม่อย่างนั้น M กับ m จะถูกนับแยกกัน แล้วหาช่องที่มากที่สุด ถ้าเทียบด้วย > ไม่ใช่ >= และไล่จากช่อง 0 ขึ้นไป ตัวที่เจอก่อนจะชนะเมื่อเสมอกันเอง

ดูเฉลย
#include <stdio.h>
#include <ctype.h>

char line[100005];

int main()
{
    int count[26] = {0};
    fgets(line, sizeof line, stdin);
    for (int i = 0; line[i] != '\0'; i++) {
        if (isalpha(line[i])) {
            count[tolower(line[i]) - 'a']++;
        }
    }
    int best = 0;
    for (int i = 1; i < 26; i++) {
        if (count[i] > count[best]) {
            best = i;
        }
    }
    printf("%c %d\n", 'a' + best, count[best]);
    return 0;
}

ข้อ 7 ถอดรหัสซีซาร์

ระดับกลาง เวลาไม่เกิน 1 วินาที

น้องได้รับข้อความลับจากเพื่อน เพื่อนเข้ารหัสด้วยการเลื่อนตัวอักษรทุกตัวไปข้างหน้า K ตัวแบบวนรอบ เลยจาก z กลับมาที่ a และเลยจาก Z กลับมาที่ A ตัวพิมพ์ใหญ่ยังเป็นตัวพิมพ์ใหญ่ ตัวพิมพ์เล็กยังเป็นตัวพิมพ์เล็ก ส่วนตัวเลข ช่องว่าง และเครื่องหมายไม่ถูกเปลี่ยน ให้ถอดรหัสกลับเป็นข้อความเดิม

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม K โดยที่ 0 ≤ K ≤ 1000000000

บรรทัดที่สองคือข้อความที่เข้ารหัสแล้ว ยาว 1 ถึง 1000 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข ช่องว่าง และเครื่องหมายวรรคตอน

ข้อมูลออก

ข้อความเดิมก่อนเข้ารหัส หนึ่งบรรทัด

3
Vhh brx dw wkh jdwh dw 7 sp.
See you at the gate at 7 pm.
29
Cheud dqg bdn
Zebra and yak
ดูคำใบ้

มีกับดักสามจุด จุดแรก บรรทัดแรกเป็นตัวเลข บรรทัดที่สองเป็นข้อความที่มีช่องว่าง ต้องอ่านทิ้งให้ถึง '\n' ก่อนเรียก fgets ตามหน้า 6.3 จุดที่สอง K ใหญ่ถึง 109 ให้ % 26 ก่อน จุดที่สาม การถอยหลัง K ตัวเท่ากับเดินหน้า 26 - K % 26 ตัว ใช้สูตร (c - 'a' + shift) % 26 + 'a' ใน int อย่าบวกลบใน char ตรงๆ

ดูเฉลย
#include <stdio.h>

char text[1005];

int main()
{
    int key;
    scanf("%d", &key);
    while (getchar() != '\n') {
    }
    fgets(text, sizeof text, stdin);
    int shift = (26 - key % 26) % 26;
    for (int i = 0; text[i] != '\0'; i++) {
        char c = text[i];
        if (c >= 'a' && c <= 'z') {
            text[i] = (c - 'a' + shift) % 26 + 'a';
        } else if (c >= 'A' && c <= 'Z') {
            text[i] = (c - 'A' + shift) % 26 + 'A';
        } else if (c == '\n' || c == '\r') {
            text[i] = '\0';
        }
    }
    printf("%s\n", text);
    return 0;
}

ข้อ 8 นับคำและคำที่ยาวที่สุด

ระดับกลาง เวลาไม่เกิน 1 วินาที

ครูให้นักเรียนเขียนเรียงความภาษาอังกฤษหนึ่งย่อหน้า แล้วพิมพ์ส่งมาเป็นบรรทัดเดียว นักเรียนบางคนเคาะช่องว่างเกินหลายครั้ง ครูอยากรู้ว่าย่อหน้านั้นมีกี่คำ และคำที่ยาวที่สุดยาวกี่ตัวอักษร

ข้อมูลเข้า

ข้อความหนึ่งบรรทัด ยาว 1 ถึง 100000 ตัว ประกอบด้วยตัวพิมพ์เล็กภาษาอังกฤษและช่องว่างเท่านั้น คำคือกลุ่มตัวอักษรที่ติดกันโดยไม่มีช่องว่างคั่น ระหว่างคำอาจมีช่องว่างหลายช่อง และหัวหรือท้ายบรรทัดอาจมีช่องว่าง

ข้อมูลออก

บรรทัดเดียว มีจำนวนคำ ตามด้วยช่องว่าง แล้วตามด้วยความยาวของคำที่ยาวที่สุด ถ้าไม่มีคำเลยให้พิมพ์ 0 0

my school  has a big   library
6 7
   hello   
1 5
ดูคำใบ้

นับจุดเริ่มคำแบบในหน้า 6.5 ไม่ใช่นับช่องว่าง เพราะช่องว่างติดกันได้หลายช่องและอาจอยู่หัวหรือท้ายบรรทัด ความยาวคำปัจจุบันเพิ่มทีละหนึ่งเมื่อเจอตัวอักษร และกลับเป็น 0 เมื่อเจอช่องว่าง อย่าลืมกรณีที่บรรทัดมีแต่ช่องว่าง

ดูเฉลย
#include <stdio.h>

char line[100005];

int main()
{
    fgets(line, sizeof line, stdin);
    int words = 0, current = 0, longest = 0;
    for (int i = 0; line[i] != '\0'; i++) {
        if (line[i] >= 'a' && line[i] <= 'z') {
            if (current == 0) {
                words++;
            }
            current++;
            if (current > longest) {
                longest = current;
            }
        } else {
            current = 0;
        }
    }
    printf("%d %d\n", words, longest);
    return 0;
}

ข้อ 9 ขยายรหัสย่อ

ระดับยาก เวลาไม่เกิน 1 วินาที

โปรแกรมส่งข้อความรุ่นเก่าประหยัดเนื้อที่ด้วยการย่อตัวอักษรที่ซ้ำติดกัน เขียนตัวอักษรหนึ่งตัวตามด้วยจำนวนครั้งที่ซ้ำ เช่น aaabcc ย่อเป็น a3b1c2 น้องได้รับข้อความที่ย่อแล้ว ให้ขยายกลับเป็นข้อความเต็ม

ข้อมูลเข้า

ข้อความย่อหนึ่งบรรทัด ยาว 2 ถึง 10000 ตัว ประกอบด้วยกลุ่มต่อกันไปเรื่อยๆ แต่ละกลุ่มคือตัวพิมพ์เล็กภาษาอังกฤษหนึ่งตัว ตามด้วยจำนวนเต็มบวกที่ไม่มีเลข 0 นำหน้า กลุ่มที่อยู่ติดกันอาจเป็นตัวอักษรเดียวกันได้ ข้อความเต็มหลังขยายยาวไม่เกิน 100000 ตัว

ข้อมูลออก

ข้อความเต็มหนึ่งบรรทัด

a3b1c2
aaabcc
x12y5
xxxxxxxxxxxxyyyyy
ดูคำใบ้

ใช้ตัวชี้ตำแหน่ง i เดินไปทีละกลุ่ม อ่านตัวอักษรหนึ่งตัว แล้วอ่านตัวเลขที่ตามมาทุกหลักด้วย count = count * 10 + (c - '0') แบบในหน้า 6.1 จนเจอตัวที่ไม่ใช่ตัวเลข ระวังตัวเลขหลายหลักอย่าง 12 หรือ 100000 และตัวเลขที่มี 0 อยู่ข้างในอย่าง 10

ดูเฉลย
#include <stdio.h>

char code[10005];

int main()
{
    scanf("%s", code);
    int i = 0;
    while (code[i] != '\0') {
        char letter = code[i];
        i++;
        int count = 0;
        while (code[i] >= '0' && code[i] <= '9') {
            count = count * 10 + (code[i] - '0');
            i++;
        }
        for (int k = 0; k < count; k++) {
            printf("%c", letter);
        }
    }
    printf("\n");
    return 0;
}

ข้อ 10 สลับอักษร

ระดับยาก เวลาไม่เกิน 1 วินาที

เกมทายคำในค่ายให้คำมาสองคำ แล้วถามว่าคำที่สองได้จากการสลับตำแหน่งตัวอักษรของคำแรกหรือไม่ เช่น listen สลับเป็น silent ได้ ตัวอักษรแต่ละตัวต้องใช้ครบทุกตัวและใช้ได้ตัวละครั้งเดียว

ข้อมูลเข้า

บรรทัดแรกคือคำแรก บรรทัดที่สองคือคำที่สอง ทั้งสองคำประกอบด้วยตัวพิมพ์เล็กภาษาอังกฤษเท่านั้น แต่ละคำยาว 1 ถึง 100000 ตัว สองคำอาจยาวไม่เท่ากัน

ข้อมูลออก

พิมพ์ YES ถ้าสลับตัวอักษรของคำแรกแล้วได้คำที่สองได้ ไม่อย่างนั้นพิมพ์ NO

listen
silent
YES
cat
cut
NO
ดูคำใบ้

วิธีตรงไปตรงมาคือไล่ตัวอักษรของคำแรกทีละตัว แล้วค้นหาตัวเดียวกันในคำที่สองที่ยังไม่ถูกใช้ ซึ่งต้องค้นทั้งคำทุกครั้ง คำยาวหนึ่งแสนตัวจึงเป็นงานราว 1010 ก้าว ช้าเกินไปมาก ลองนับแทน ใช้อาเรย์ 26 ช่องนับตัวอักษรของแต่ละคำแบบในหน้า 6.5 แล้วเทียบอาเรย์ทั้งสองทีละช่อง

ดูเฉลย
#include <stdio.h>

char first[100005], second[100005];

int main()
{
    scanf("%s %s", first, second);
    int count[26] = {0};
    for (int i = 0; first[i] != '\0'; i++) {
        count[first[i] - 'a']++;
    }
    for (int i = 0; second[i] != '\0'; i++) {
        count[second[i] - 'a']--;
    }
    int same = 1;
    for (int i = 0; i < 26; i++) {
        if (count[i] != 0) {
            same = 0;
        }
    }
    if (same) {
        printf("YES\n");
    } else {
        printf("NO\n");
    }
    return 0;
}