C语言练习题(二)
C语言练习题(二)
题目一
描述
对于给定的仅由小写字母构成的字符串 s,将其倒过来输出。
输入描述:
在一行上输入一个长度 1≦length(s)≦10^3,仅由小写字母构成的字符串 s。
输出描述:
在一行上输出一个字符串,代表颠倒后的字符串。

#include <stdio.h>
#include <string.h>int main() {char s[1001]; // 字符串长度不超过1000// 读取字符串scanf("%s", s);int len = strlen(s);// 方法1:直接逆序输出for (int i = len - 1; i >= 0; i--) {printf("%c", s[i]);}printf("\n");return 0;
}

题目二
描述
对于给定的若干个单词组成的句子,每个单词均由大小写字母混合构成,单词间使用单个空格分隔。输出以单词为单位逆序排放的结果,即仅逆序单词间的相对顺序,不改变单词内部的字母顺序。
输入描述:
在一行上输入若干个字符串,每个字符串代表一个单词,组成给定的句子。
除此之外,保证每个单词非空,由大小写字母混合构成,且总字符长度不超过 10^3。
输出描述:
在一行上输出一个句子,代表以单词为单位逆序排放的结果。

#include <stdio.h>
#include <string.h>#define MAX_LENGTH 1001int main() {char sentence[MAX_LENGTH];char words[500][50]; // 假设最多500个单词,每个单词最多50个字符int word_count = 0;fgets(sentence, sizeof(sentence), stdin);// 去除末尾的换行符int len = strlen(sentence);if (len > 0 && sentence[len-1] == '\n') {sentence[len-1] = '\0';}// 手动分割单词int start = 0;for (int i = 0; i <= len; i++) {if (sentence[i] == ' ' || sentence[i] == '\0') {// 复制单词int word_len = i - start;if (word_len > 0) {strncpy(words[word_count], &sentence[start], word_len);words[word_count][word_len] = '\0';word_count++;}start = i + 1;}}// 逆序输出单词for (int i = word_count - 1; i >= 0; i--) {printf("%s", words[i]);if (i > 0) {printf(" ");}}printf("\n");return 0;
}
题目三
描述
对于给定的由大小写字母混合构成的 n 个单词,输出按字典序从小到大排序后的结果。
【名词解释】
从字符串的第一个字符开始逐个比较,直至发现第一个不同的位置,比较这个位置字符的 Ascii 码,Ascii 码较小(“‘A’”<“‘B’“⋯<”‘Z’”<“‘a’”<⋯<“‘z’” )的字符串字典序也较小。
输入描述:
第一行输入一个整数 n(1≦n≦10^3) 代表给定的单词个数。
此后 n 行,每行输入一个长度 1≦length(s)≦100,由大小写字母构成的字符串 s,代表一个单词。
输出描述:
一共 n 行,每行输出一个字符串,代表排序后的结果。第一行输出字典序最小的单词。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define MAX_WORDS 1000
#define MAX_LENGTH 101// 比较函数,用于qsort排序
int compare(const void *a, const void *b) {return strcmp((const char*)a, (const char*)b);
}int main() {int n;char words[MAX_WORDS][MAX_LENGTH];// 读取单词数量scanf("%d", &n);// 读取所有单词for (int i = 0; i < n; i++) {scanf("%s", words[i]);}// 使用qsort进行排序qsort(words, n, sizeof(words[0]), compare);// 输出排序后的结果for (int i = 0; i < n; i++) {printf("%s\n", words[i]);}return 0;
}

题目四
描述
对于给定的 int 型的十进制整数 n,统计其在内存中存储时 1 的个数。换句话说,即统计其二进制表示中 1 的个数。
输入描述:
在一行上输入一个整数 n(0≦n<2^31),代表给定的数字。
输出描述:
在一行上输出一个整数,代表 n 的二进制表示中 1 的个数。

#include <stdio.h>int main() {int n;scanf("%d", &n);int count = 0;while (n) {count += n & 1; // 检查最低位是否为1n >>= 1; // 右移一位}printf("%d\n", count);return 0;
}
更高效
#include <stdio.h>int main() {int n;scanf("%d", &n);int count = 0;while (n) {n &= (n - 1); // 这个操作会消除n中最右边的1count++;}printf("%d\n", count);return 0;
}

题目五
描述
你需要书写一个程序验证给定的密码是否合格。
合格的密码要求:
长度不少于 8 位
必须包含大写字母、小写字母、数字、特殊字符中的至少三种
不能分割出两个独立的、长度大于 2 的连续子串,使得这两个子串完全相同;更具体地,如果存在两个长度大于 2 的独立子串 s_1,s_2 ,使得 s_1=s_2 ,那么密码不合法。
子串为从原字符串中,连续的选择一段字符(可以全选、可以不选)得到的新字符串。
可见字符集为 ASCII 码在 33 到 126 范围内的可见字符。您可以参阅下表获得其详细信息(您可能关注的内容是,这其中不包含空格、换行)。
输入描述:
本题将会给出 1≦T≦10 组测试数据,确切数字未知,您需要一直读入直到文件结尾;每组测试数据描述如下:
在一行上输入一个长度为 1≦length(s)≦100 ,由可见字符构成的字符串 s ,代表待判断的密码。
输出描述:
对于每一组测试数据,新起一行。若密码合格,输出 “OK” ,否则输出 “NG” 。
#include <stdio.h>
#include <string.h>
#include <ctype.h>// 检查密码是否包含至少三种字符类型
int check_char_types(const char *password) {int has_upper = 0, has_lower = 0, has_digit = 0, has_special = 0;for (int i = 0; password[i] != '\0'; i++) {if (isupper(password[i])) {has_upper = 1;} else if (islower(password[i])) {has_lower = 1;} else if (isdigit(password[i])) {has_digit = 1;} else if (password[i] >= 33 && password[i] <= 126) {has_special = 1;}}int type_count = has_upper + has_lower + has_digit + has_special;return type_count >= 3;
}// 检查是否存在长度大于2的重复子串
int has_repeating_substrings(const char *password) {int len = strlen(password);// 检查所有可能的子串长度(3到len/2)for (int sub_len = 3; sub_len <= len / 2; sub_len++) {// 检查所有可能的起始位置for (int i = 0; i <= len - 2 * sub_len; i++) {char substr1[sub_len + 1];strncpy(substr1, password + i, sub_len);substr1[sub_len] = '\0';// 在剩余字符串中查找相同的子串for (int j = i + sub_len; j <= len - sub_len; j++) {char substr2[sub_len + 1];strncpy(substr2, password + j, sub_len);substr2[sub_len] = '\0';if (strcmp(substr1, substr2) == 0) {return 1; // 找到重复子串}}}}return 0; // 没有找到重复子串
}// 主验证函数
void validate_password(const char *password) {// 检查长度if (strlen(password) < 8) {printf("NG\n");return;}// 检查字符类型if (!check_char_types(password)) {printf("NG\n");return;}// 检查重复子串if (has_repeating_substrings(password)) {printf("NG\n");return;}// 所有检查通过printf("OK\n");
}int main() {char password[101];// 读取所有输入直到文件结尾while (fgets(password, sizeof(password), stdin)) {// 去除换行符password[strcspn(password, "\n")] = 0;if (strlen(password) == 0) {continue;}validate_password(password);}return 0;
}
题目六
描述
某商店规定:三个空汽水瓶可以换一瓶汽水,允许向老板借空汽水瓶(但是必须要归还)。
小张手上有 n 个空汽水瓶,她想知道自己最多可以喝到多少瓶汽水。
输入描述:
在一行上输入一个整数 n(0≦n≦100) ,代表小张手上的空汽水瓶数量。特别地,n=0 代表输入结束,您只需要立即退出,不需要针对这种情况进行处理。
输出描述:
对于每一组测试数据,新起一行。输出一个整数,代表小张最多可以喝到的汽水数量。
#include <stdio.h>int main() {int n;while (scanf("%d", &n) != EOF && n != 0) {printf("%d\n", n / 2);}return 0;
}

题目七
描述
对于给定的仅由小写字母构成的字符串,删除字符串中出现次数最少的字符。输出删除后的字符串,字符串中其它字符保持原来的顺序。
特别地,若有多个字符出现的次数都最少,则把这些字符都删除。
输入描述:
在一行上输入一个长度为 1≦length(s)≦20 ,仅由小写字母构成的字符串 s ,代表待处理的字符串。
输出描述:
在一行上输出一个字符串,代表删除后的答案。保证这个字符串至少包含一个字符。

#include <stdio.h>
#include <string.h>int main() {char s[21];scanf("%s", s);int count[26] = {0}; // 用于统计每个小写字母出现的次数int len = strlen(s);// 统计每个字符出现的次数for (int i = 0; i < len; i++) {count[s[i] - 'a']++;}// 找出最小出现次数(只考虑出现过的字符)int min_count = 21; // 初始化为一个大于字符串长度的值for (int i = 0; i < 26; i++) {if (count[i] > 0 && count[i] < min_count) {min_count = count[i];}}// 构建结果字符串char result[21];int index = 0;for (int i = 0; i < len; i++) {if (count[s[i] - 'a'] != min_count) {result[index++] = s[i];}}result[index] = '\0';printf("%s\n", result);return 0;
}


题目八
描述
对于给定的由可见字符和空格组成的字符串,按照下方的规则进行排序:
按照字母表中的顺序排序(不区分大小写);
同一字母的大小写同时存在时,按照输入顺序排列;
非字母字符保持原来的位置不参与排序;
直接输出排序后的字符串。
输入描述:
在一行上输入一个长度为 1≦length(s)≦1000 ,由上表中的字符组成的字符串 s 。
输出描述:
输出一个字符串,代表按照规则排序后的字符串。

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>int main() {char s[1001];fgets(s, sizeof(s), stdin);int len = strlen(s);if (len > 0 && s[len-1] == '\n') {s[len-1] = '\0';len--;}// 提取所有字母字符及其原始位置char *letters = (char*)malloc(len * sizeof(char));int *positions = (int*)malloc(len * sizeof(int));int letter_count = 0;for (int i = 0; i < len; i++) {if (isalpha(s[i])) {letters[letter_count] = s[i];positions[letter_count] = i;letter_count++;}}// 对字母进行排序(稳定排序,不区分大小写)// 使用冒泡排序实现稳定排序for (int i = 0; i < letter_count - 1; i++) {for (int j = 0; j < letter_count - 1 - i; j++) {// 比较时不区分大小写if (tolower(letters[j]) > tolower(letters[j+1])) {// 交换char temp = letters[j];letters[j] = letters[j+1];letters[j+1] = temp;}}}// 构建结果字符串char result[1001];int letter_index = 0;for (int i = 0; i < len; i++) {if (isalpha(s[i])) {// 当前位置是字母,使用排序后的字母result[i] = letters[letter_index++];} else {// 非字母字符保持原样result[i] = s[i];}}result[len] = '\0';printf("%s\n", result);free(letters);free(positions);return 0;
}


题目九
描述
定义两个正整数 a 和 b 是“素数伴侣”,当且仅当 a+b 是一个素数。
现在,密码学会邀请你设计一个程序,从给定的 n 个正整数 {a_1,a_2,…,a_n} 中,挑选出最多的“素数伴侣”,你只需要输出挑选出的“素数伴侣”对数。保证 n 为偶数,一个数字只能使用一次。
输入描述:
第一行输入一个正偶数 n(2≦n≦100) 代表数字个数。
第二行输入 n 个正整数 a_1,a_2,…,a_n (1≦a_i≦3×10^4) 代表给定的数字。
输出描述:
输出一个整数,代表最多可以挑选出的“素数伴侣”的数量。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>#define MAX_N 100
#define MAX_SUM 60000// 全局变量
bool isPrime[MAX_SUM + 1];
int graph[MAX_N][MAX_N];
int match[MAX_N];
bool visited[MAX_N];// 埃拉托斯特尼筛法预处理素数
void sieve() {memset(isPrime, true, sizeof(isPrime));isPrime[0] = isPrime[1] = false;for (int i = 2; i * i <= MAX_SUM; i++) {if (isPrime[i]) {for (int j = i * i; j <= MAX_SUM; j += i) {isPrime[j] = false;}}}
}// 深度优先搜索进行二分图匹配
bool dfs(int u, int right_size) {for (int v = 0; v < right_size; v++) {if (graph[u][v] && !visited[v]) {visited[v] = true;if (match[v] == -1 || dfs(match[v], right_size)) {match[v] = u;return true;}}}return false;
}// 匈牙利算法求最大匹配
int hungarian(int left_size, int right_size) {memset(match, -1, sizeof(match));int result = 0;for (int u = 0; u < left_size; u++) {memset(visited, false, sizeof(visited));if (dfs(u, right_size)) {result++;}}return result;
}int main() {int n;scanf("%d", &n);int arr[MAX_N];for (int i = 0; i < n; i++) {scanf("%d", &arr[i]);}// 预处理素数表sieve();// 分类存储数字int A_count = 0; // 1的个数int B_count = 0; // 大于1的奇数个数int C_count = 0; // 偶数个数int B_values[MAX_N], C_values[MAX_N];for (int i = 0; i < n; i++) {if (arr[i] == 1) {A_count++;} else if (arr[i] % 2 == 1) {B_values[B_count++] = arr[i];} else {C_values[C_count++] = arr[i];}}// 计算1的内部配对数int pair1 = A_count / 2;int remain1 = A_count % 2;// 构建左边节点数组int left_size = B_count + remain1;int left_values[MAX_N];// 复制B类节点for (int i = 0; i < B_count; i++) {left_values[i] = B_values[i];}// 如果还有剩余的1,加入左边if (remain1 > 0) {left_values[B_count] = 1;}// 构建二分图memset(graph, 0, sizeof(graph));for (int i = 0; i < left_size; i++) {for (int j = 0; j < C_count; j++) {if (isPrime[left_values[i] + C_values[j]]) {graph[i][j] = 1;}}}// 计算二分图最大匹配int pair2 = hungarian(left_size, C_count);// 输出总配对数printf("%d\n", pair1 + pair2);return 0;
}


题目十
对于给定的若干个单词组成的句子,每个单词均由大小写字母构成,单词间使用非字母字符分隔。输出以单词为单位逆序排放的结果,即仅逆序单词间的相对顺序,不改变单词内部的字母顺序。
特别地,在输出结果中,去除原有的分隔符,转而使用单个空格间隔单词。
输入描述:
在一行上输入若干个字符串,每个字符串长度为 1≦length(s)≦20 ,仅由大小写字母构成,代表一个单词。单词间还夹杂了一定数量的非字母字符(但保证是可见字符),代表分隔符。
除此之外,保证总字符长度不超过 10^4 。
输出描述:
在一行上输出一个句子,代表以单词为单位逆序排放的结果。单词间使用单个空格分隔。

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>#define MAX_WORDS 500
#define MAX_WORD_LEN 21int main() {char input[10001];fgets(input, sizeof(input), stdin);// 去除换行符input[strcspn(input, "\n")] = '\0';char words[MAX_WORDS][MAX_WORD_LEN];int word_count = 0;int len = strlen(input);// 提取单词int i = 0;while (i < len) {// 跳过非字母字符while (i < len && !isalpha(input[i])) {i++;}// 提取单词if (i < len) {int j = 0;while (i < len && isalpha(input[i])) {if (j < MAX_WORD_LEN - 1) {words[word_count][j++] = input[i++];} else {i++; // 如果单词超长,跳过剩余字符}}words[word_count][j] = '\0';word_count++;// 防止数组越界if (word_count >= MAX_WORDS) {break;}}}// 逆序输出单词for (int i = word_count - 1; i >= 0; i--) {printf("%s", words[i]);if (i > 0) {printf(" ");}}printf("\n");return 0;
}


