从本周开始进行编程训练,每周至少有5天的时间用来coding,每天
至少提交3道题,另外需要对刷过的题进行总结,不能刷过一遍什么都
没留下或者印象不深刻,此系列博客作总结用。
当前题集:牛客网华为机试题
编程环境:sublime text3 + vmware ubuntu + xshell
编程语言:C
文章目录
字符串相关
题1 字符串最后一个单词长度
计算字符串最后一个单词的长度,单词以空格隔开。
解题思路:用两个字符指针phead ptail, ptail指向最后一个单词的最后一个字符,phead从ptail指向的位置往前找,直到遇到空白符停止,即可根据phead, ptail来计算最后一个单词长度。另外学会了使用一个很重要的函数,fgets
,原来一直使用gets函数从标准输入中读取一行,但是其并不安全,因为没有限制读入的字符串的长度,fgets
可以通过第二个参数限定读入字符串的长度,另外需要注意的是fgets
读取一行并不会将\n转换成\0,而是将其作为一个字符读入,打印进行测试会输出换行符,最后当读取错误或者遇到EOF会返回NULL,因此具体判断的时候需要使用feof(stream)
ferror(stream)
来进行具体判断。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 5010
char inputStr[MAXLEN];
int main(int argc, char *argv[])
{
if(fgets(inputStr, MAXLEN, stdin) == NULL){
printf("Function fgets Error!\n");
exit(-1);
}
char *ptail, *phead;
int len = strlen(inputStr);
ptail = inputStr + len - 2;
//case easy to be ignored
while(*ptail == ' ' || *ptail == '\t')
ptail--;
for(phead = ptail; phead != inputStr && *phead != ' ' && *phead != '\t'; phead--) ;
if(phead != inputStr){
phead++;
}
printf("%d\n", ptail - phead + 1);
return 0;
}
题2 统计字符出现个数
写出一个程序,接受一个由字母和数字组成的字符串,和一个字符,然后输出输入字符串中含有该字符的个数。不区分大小写.
解题思路:此题很简单,遍历字符数组即可,需要注意的是如果字符是字母的话不区分大小写。
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
const int maxn = 1010;
int main(int argc, char *argv[])
{
int cnt = 0, len;
char inputStr[maxn], c;
scanf("%s %c", inputStr, &c);
len = strlen(inputStr);
bool isLetters = false;
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')){
isLetters = true;
}
for(int i = 0; i < len; i++){
if(!isLetters){
inputStr[i] == c ? cnt++ : NULL;
}
else{
if(c >= 'a' && c <= 'z'){
(inputStr[i] == c || inputStr[i] == c - 'a' + 'A') ? cnt++ : NULL;
}
else{
(inputStr[i] == c || inputStr[i] == c - 'A' + 'a') ? cnt++ : NULL;
}
}
}
printf("%d\n", cnt);
return 0;
}
题4 字符串拆分
- 连续输入字符串,请按长度为8拆分每个字符串后输出到新的字符串数组;
- 长度不是8整数倍的字符串请在后面补数字0,空字符串不处理。
解题思路 :每一行字符串的处理是独立的,最后不足8个字符的补0即可。
#include <stdio.h>
const int maxn = 110;
char inputStr[maxn];
int main(int argc, char *argv[])
{
while(fgets(inputStr, maxn, stdin) != NULL)
{
int cnt = 0, i = 0;
while(inputStr[i] != '\n')
{
if(cnt == 8){
cnt = 0;
printf("\n");
}
putchar(inputStr[i]);
cnt++;
i++;
}
while(cnt != 8){
putchar('0');
cnt++;
}
printf("\n");
}
/*
if(feof(stdin)){
printf("Reach the end of stdin.\n");
}
if(ferror(stdin)){
printf("fgers error.\n");
}
*/
return 0;
}
题5 进制转换
写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串。(多组同时输入)
解题思路:这一道题被我简化了,并没有考虑大数的情况,int对应的量级为10^9,
long long对应的量级是10^18, 因此如果测试用例中超过了这个范围就肯定会出错,所以需要回顾大数乘法以及加法的实现。
#include <stdio.h>
//Consider int=> 10^9, long long=> 10^18. If the current problem related with big number realization,
//the following solution may not pass the test. However, I will not consider big number realization fisrt,
//Because I have forgotten the realization of it.
const int maxn = 15;
char inputStr[maxn];
int main(int argc, char *argv[])
{
while(scanf("%s", inputStr) != EOF)
{
int radix = 16, i = 0;
long long result = 0;
char c;
while((c = inputStr[i]) != '\0')
{
if(c >= 'a' && c <= 'f')
{
result *= 16;
result += c - 'a' + 10;
}
else if(c >= 'A' && c <= 'F')
{
result *= 16;
result += c - 'A' + 10;
}
else if(c >= '0' && c <= '9')
{
result *= 16;
result += c - '0';
}
i++;
}
printf("%lld\n", result);
}
return 0;
}
简单hash表相关
结合自己刷题的经验来说,很多题目都可以用hashtable来解决,即最简单的用空间换取时间的策略。常见题型比如去重、排序、合并相同索引的表记录等等。
比如在这周刷到的9道题,其中的3道题使用hashtable就很简单的解决了。具体思路不再赘述。
题9 提交不重复的整数
输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。
用bool isPrint[maxn];数组来标记是否已经打印
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
bool isPrint[10];
int main(int argc, char *argv[])
{
int n;
while(scanf("%d", &n) != EOF)
{
memset(isPrint, false, 10 * sizeof(bool));
while(n)
{
int r = n % 10;
if(!isPrint[r]){
putchar(r + '0');
isPrint[r] = true;
}
n = n / 10;
}
}
return 0;
}
题8:合并表记录
数据表记录包含表索引和数值,请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算,输出按照key值升序进行输出。
用一个数组hashtable作为简单的hash表,索引即数组下标,数组值即为key对应的value.
#include <stdio.h>
#include <string.h>
const int maxn = 10010;
int hashtable[maxn];
int main(int argc, char *argv[])
{
memset(hashtable, -1, maxn * sizeof(int));
//printf("%d\n", hashtable[10000]);
int n, key, value;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++){
scanf("%d %d", &key, &value);
if(hashtable[key] == -1){
hashtable[key] = value;
}
else{
hashtable[key] += value;
}
}
//====print=====
for(int i = 0; i < maxn; i++){
if(hashtable[i] != -1){
printf("%d %d\n", i, hashtable[i]);
}
}
}
return 0;
}
题2:明明的随机数
思路相同。
最有收获的一道题:数学相关
题6 质数因子
输入一个long型整数,按照从小到大的顺序输出它的所有质数的因子,以空格隔开。最后一个数后面也要有空格。
coding
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
bool judgePrim(long n);
//remeber that allocating 10010 units for prime tables is enough for int.
const int maxn = 10010;
struct fac{
long primNum;
int cnt;
};
int main(int argc, char *argv[])
{
long n;
//struct fac facArray[n];
struct fac facArray[100];
int pindex = 0;
while(scanf("%ld", &n) != EOF)
{
if(n == 1){
printf("1 = 1\n");
continue;
}
//if n is a large number, no enough space for isPrim causes segment fault.
//bool isPrim[n+1];
//memset(isPrim, 1, n+1);
long isPrim[maxn];
int pnum = 0;
memset(isPrim, 1, maxn);
long index = 2;
pindex = 0;
/* 如果不能申请包含所有n个数的空间,埃式筛法不再适用
while(index <= n)
{
if(isPrim[index] == false){
index++;
continue;
}
for(long times = 2; times * index <= n; times++)
{
isPrim[index * times] = false;
}
index++;
}
*/
//find all prime numbers smaller than sqrt(n)
for(long i = 2; i < maxn; i++){
if(judgePrim(i)){
isPrim[pnum++] = i;
}
}
long sqr = (long)sqrt(1.0 * n);
long result = n;
for(int k = 0; k < pnum && isPrim[k] <= sqr ; k++){
if(result % isPrim[k] == 0){
facArray[pindex].primNum = isPrim[k];
facArray[pindex].cnt = 0;
while(result % isPrim[k] == 0){
result /= isPrim[k];
facArray[pindex].cnt++;
}
pindex++;
if(result == 1)
break;
}
}
if(result != 1){
facArray[pindex].primNum = result;
facArray[pindex].cnt = 1;
pindex++;
}
//----------print result------------
for(int i = 0; i < pindex; i++){
int times = facArray[i].cnt;
while(times--){
printf("%ld ", facArray[i].primNum);
}
}
printf("\n");
}
return 0;
}
bool judgePrim(long num)
{
bool ret = true;
long sqr = (long)sqrt(1.0 * num);
for(long i = 2; i <= sqr; i++)
{
if(num % i == 0)
{
ret = false;
break;
}
}
return ret;
}
评价:可以看到上面的代码中有很多的注释掉的内容,实际上这道题花费了我大概2个多小时的时间,有下面几点思路以及实际编程上的收获:
- 首先怎么判断一个数是素数,这个还是很简单的,即从2到sqrt(n)(可取等于号),只有有一个能整除n那么就不是素数,反之就是素数。
- 怎么求一个数的所有质因数:
- 我们知道,对于一个正整数n来说,如果它存在1和本身之外的因子,那么一定是在sqrt(n)的左右成对出现的。而这里把这个结论用到“质因子”上面,会得到一个强化的结论:对一个正整数n来说,如果它存在[2,n]范围内的质因子,要么这些质因子全部小于等于sqrt(n),要么只存在一个大于sqrt(n)的质因子,而其余的全部小于等于sqrt(n),这就给质因子的求解提供了下列思路:
- 枚举1-sqrt(n)范围内的所有质数p,判断p是否是n的因子:
- 是,那么给fac数组中增加质因子p,并初始化其个数为0,然后只要p还是n的因子,就让n不断除以p,每次操作另p的个数加1,直到p不是n的因子为止。
- 否,直接跳过
- 如果在上面步骤结束后n仍然大于1,说明n有且仅有一个大于sqrt(n)的质因子,这时需要将这个质因子加入到fac数组中,并另其个数为1.
- 在获取素数表的时候有两种方法:一种是直接判断2到n-1中的每一个数是不是素数,是则添加到素数表中;第二种方法是埃式筛法,即需要一个全局的isPrim表,从2到的倍数筛起(不是素数),筛完发现3并没有被筛掉,因而3是素数,再筛掉3的倍数,依次类推,但是在编程过程中,第二种埃式筛法存在局限性,因为需要一个全局数组,在实际要分解质因子数值很大的情况下会出现segment fault,所以需要慎重考虑。
总结:
这一周刷的题比较基础,但是如果深挖还是有一些东西的,比如怎么求质因数,还有大数的乘法实现。另外,感觉用纯c去编程的话还是很多地方需要自己去造轮子,并不是很方便,另外自己用纯c的话主要是因为自己对于c++里面的stl记得不是很清楚,用的还是少了,在一些算法的实现中比如 图论里面 使用vector之类的还是很方便的,所以接下来的编程训练中,涉及到栈、数组之类的可以多用用stl里面提供的容器,使代码更加优雅。等毕设做完之后开始回顾一下Java以及学习一下高级用法,便于以后用Java刷leetcode。