coding训练-week1(4.29-5.5)

从本周开始进行编程训练,每周至少有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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值