【牛客网】华为机试题(00、C++版本)

华为机试题,本来以为对一些基础的程序还是有点信心的;不过不练不知道,一练吓一跳。平时虽然也都码程序,但是一到机试题就各种乱七八的不适应。还是要加强一下练习。

1、字符串最后一个单词的长度

题目描述:计算字符串最后一个单词的长度,单词以空格隔开。

例如:输入Hello World,输出5。

解答:这道题主要是练习一下字符串的输入和指定字符的查找。

#include <iostream>
using namespace std;
#include <string>

int main(){
    string s;
    getline(cin,s);
    int j=0;
    for(int i=s.length()-1; i>=0; i--){
        if(s[i]!=' '){
            j++;
        }else{
            break;
        }
    }
    cout<<j;
    return 0;
}

如果熟悉C++的各种函数的话,可以使用String的rfind()函数(寻找某个字符最后一次出现的序号)、find()函数(寻找某个字符第一次出现的序号)。

#include <iostream>
using namespace std;
#include <string>

int main() {
	string s;
	getline(cin, s);
	int j;
	j = s.length() - 1 - s.rfind(' ');
	cout << j;
	return 0;
}

2、计算字符个数

题目描述:写出一个程序,接受一个有字母和数字以及空格组成的字符串,和一个字符,然后输出输入字符串中含有该字符的个数。不区分大小写。

例如:输入ABCDEF A,输出1。

扫描二维码关注公众号,回复: 2473636 查看本文章

解答:这道题主要是练习一下大小写的改写。

#include <iostream>
using namespace std;
#include <string>

int main()
{
	string s;
	getline(cin, s);
	char c;
	cin >> c;
	int count = 0;
	for (int i = 0; i<s.length(); i++) {
		if (s.at(i)== toupper(c)|| s.at(i) == tolower(c))
			count++;
	}
	cout << count;
	return 0;
}

或者,也可以使用C语言来编写:

#include <stdio.h>
#include <string.h>
  
int main()
{
   char str[10000];
   char ch;
   int len = 0;
   gets(str); 
   ch = getchar();              //scanf("%c",&ch); 
   for(int i = 0; i < strlen(str); i++)
   {
            if(str[i] == ch || str[i] == ch -32 || str[i] == ch + 32)
                len++;
   }
   printf("%d\n",len);
   return 0;
}

注意一点:在C++中字符串长度可以用.size()、.length()方法;而strlen(char *),若放入一个字符串常量是可以求长度的,但是若放入一个字符串变量则不行。这三个方法都不计算字符串最后的结束符'\0',甚至可以说是,遇到第一个字符串结束符'\0'就停止计算个数了。

注意:如果使用sizeof()来计算字符串变量的长度是不正确的!C++标准库中的string类的对象在创建时会保留额外的内存空间,以便于用户调用append成员函数或者给string对象重新赋值时不会发生越界行为。所以,最小默认为28。

sizeof 操作函数类型:

int f1(){return 0;};
double f2(){return 0.0;}
void f3(){}

cout<<sizeof(f1())<<endl;     // f1()返回值为int,因此被认为是int
cout<<sizeof(f2())<<endl;     // f2()返回值为double,因此被认为是double
cout<<sizeof(f3())<<endl;     // 错误!无法对void类型使用sizeof
cout<<sizeof(f1)<<endl;       // 错误!无法对函数指针使用sizeof   
cout<<sizeof*f2<<endl;        // *f2,和f2()等价,被认为是double

3、明明的随机数

题目描述:明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤1000),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作(同一个测试用例里可能会有多组数据,希望大家能正确处理)。

例如:输入11 10 20 40 32 67 40 20 89 300 400 15,输出10 15 20 32 40 67 89 300 400。

解答:原本做这道题就是很机械地排序来做:

#include <iostream>
using namespace std;

int main()
{
	int count;
	while (cin >> count) {
		int temp, flag = 0;
		int num[1000];
		for (int i = 0; i < count; i++) {
			cin >> num[i];
		}
		for (int i = 0; i < count - 1; i++) {
			for (int j = i + 1; j < count - flag; j++) {
				if (num[i] > num[j]) {
					temp = num[i];
					num[i] = num[j];
					num[j] = temp;
				}
				if (num[i] == num[j]) {
					flag++;
					for (int k = j; k < count - 1; k++) {
						num[k] = num[k + 1];
					}
					j--;
				}
			}
		}
		for (int i = 0; i < count - flag; i++) {
			cout << num[i] << endl;
		}
	}
	return 0;
}

看得出来,算法复杂度很高。后来发现了一个很巧妙的方法,通过计数排序来完成:

#include <iostream>
using namespace std;

int main() {
    int N, n;
    while (cin >> N) {
        int a[1001] = { 0 };
        while (N--) {
            cin >> n;
            a[n] = 1;
        }
        for (int i = 0; i < 1001; i++)
            if (a[i])
                cout << i << endl;
    }
    return 0;
}

这个方法在之前大疆的笔试题中也遇到了:【嵌入式基础】2014大疆嵌入式笔试题(附超详细解答,上篇)

4、字符串分隔

题目描述:连续输入两个字符串,请按长度为8拆分每个字符串后输出到新的字符串数组; 长度不是8整数倍的字符串请在后面补数字0,空字符串不处理。 

例如:输入abc 123456789,输出abc00000 12345678 90000000。

解答:拆分字符串,很传统的方法是:

#include <iostream>
using namespace std;
#include <string>

void fun(string s) 
{
	char str[13][8];
	int num = 0, count = 0;
	for (int j = 0; j < s.length(); j++) {
		if (count == 8) {
			count = 0;
			num++;
		}
		str[num][count] = s.at(j);
		count++;
	}
	while (count != 8) {
		str[num][count] = '0';
		count++;
	}
	for (int i = 0; i < num + 1; i++) {
		for (int j = 0; j < 8; j++) {
			cout << str[i][j];
		}
		cout << endl;
	}
}

int main() 
{
	string s1, s2;
	getline(cin, s1);
	getline(cin, s2);
	fun(s1);
	fun(s2);

	return 0;
}

这样做虽然也能获得答案,但是很明显,还是有点绕的。其实可以利用string的substr()方法来解决:

#include <iostream>
using namespace std;
#include <string>

void fun(string s) 
{
	while (s.size() > 8) {
		cout << s.substr(0, 8) << endl;
		s = s.substr(8);
	}
	cout << s.append(8 - s.size(), '0') << endl;
}
int main() {
	string s1, s2;
	getline(cin, s1);
	getline(cin, s2);
	fun(s1);
	fun(s2);

	return 0;
}

瞬间感觉整个程序都清晰了许多!下面分析一下string的substr()和substring()的区别:

  • stringvar.substr(start , [length]):索引,长度
  • strVariable.substring(start, end):索引,索引

说明:

  • 如果substr()函数的length为0或负数,将返回一个空字符串。如果没有指定该参数,则子字符串将延续到 stringvar 的最后;
  • substring()方法将返回一个包含从start 到最后(不包含end)的子字符串的字符串;
  • substring()方法使用start和end两者中的较小值作为子字符串的起始点。

5、进制转换

题目描述:写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串。(多组同时输入 )

例如:输入0xA,输出10。

解答:采取进制转换的计算方法,但是需要注意的是这里的方法不是那种死板的计算方法:

#include <iostream>
using namespace std;
#include <string>

int main() {
	string str;
	while (getline(cin, str)) {
		long num = 0; 
		for (int i = 2; i < str.length(); ++i) {
			if (str[i] >= '0' && str[i] <= '9')
				num = num * 16 + (str[i] - '0');
			else
				num = num * 16 + (str[i] - 'A' + 10);
		}
		cout << num << endl;
	}

	return 0;
}

但是这样的做法,不能够对输入的数值进行十六进制的判断。当然,本题还有一个非常投巧的方法,利用输入的办法来进行数制转换,而且可以完成十六进制的判断(hex:十六进制、oct:八进制、dev:十进制)

#include <iostream>
using namespace std;

int main()
{
    int a;
    while(cin>>hex>>a){
        cout<<a<<endl;
    }
}

利用C语言也可以实现:

#include <stdio.h>

int main() 
{
	int a;
	while (scanf_s("%X", &a)) {
		printf("%d\n", a);
	}
}

6、质数因子

题目描述:输入一个正整数,按照从小到大的顺序输出它的所有质数的因子,最后一个数后面也要有空格。

例如:输入180,输出2 2 3 3 5 。

解答:这道看似很简单的题目,原来也是有坑的!刚开始的思路就是按部就班地循环--判断是否为质数--判断是否是因子:

#include <iostream>
using namespace std;

int main()
{
	long num;
	cin >> num;
	for (int i = 2; i <= num; i++) {
		bool flag = true;
		for (int j = 2; j < i; j++) {
			if (i%j == 0) {
				flag = false;
				break;
			}
		}
		if (flag) {
			if (num%i == 0) {
				num = num / i;
				cout << i << ' ';
				i--;
			}
		}
	}

	return 0;
}

后来看了别人写的程序,突然发现自己智商可能真的不够用:

#include <iostream>
using namespace std;

int main(void)
{
	long input;
	while (cin >> input)
	{
		while (input != 1)
		{
			for (int i = 2; i <= input; i++)
			{
				if (input % i == 0)
				{
					input /= i;
					cout << i << ' ';
					break;
				}
			}
		}

	}

	return 0;
}

这个程序算不上完美,每次更新完input之后又都从2开始循环,在这一点上明显复杂度比我的代码高了不少。但是,这个程序让我突然发现,这道题目其实和是不是质数没有关系!从2开始循环,如果都有一个非质数因子,那么在这个因子的前面,该非质数的质数因子又是怎么能“幸免于难”的呢?

也就是说,这道题的程序只要如下就行了:

#include <iostream>
using namespace std;

int main()
{
	long num;
	cin >> num;
	for (int i = 2; i <= num; i++) {
		if (num%i == 0) {
			num = num / i;
			cout << i << ' ';
			i--;
		}
	}

	return 0;
}

7、取近似值

题目描述:写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整;小于5,则向下取整。

例如:输入5.5,输出6。

解答:float强制转换成int为截取。

#include <iostream>
using namespace std;

int main()
{
	float f;
	cin >> f;
	if ((f - (int)f) >= 0.5)
		cout << (int)f + 1;
	else
		cout << (int)f;

	return 0;
}

后来看见网友的一个答案,瞬间觉得很赞,算是开拓一下:

#include <iostream>
using namespace std;

int main()
{
	float a;
	cin >> a;
	cout << int(a + 0.5);

	return 0;
}

8、合并表记录

题目描述:数据表记录包含表索引和数值,请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算,输出按照key值升序进行输出。

例如:输入4 0 1 0 2 1 2 3 4,输出0 3 1 2 3 4。

解答:用数组来装数据表记录进行解答。

#include <iostream>
using namespace std;

int main()
{
	int a[1000][2];
	int num;
	int temp;
	cin >> num;
	for (int i = 0; i < num; i++) {
		cin >> a[i][0] >> a[i][1];
	}
	for (int i = 0; i < num - 1; i++) {
		for (int j = i + 1; j < num ; j++) {
			if (a[i][0] > a[j][0]) {
				temp = a[i][0];
				a[i][0] = a[j][0];
				a[j][0] = temp;
				temp = a[i][1];
				a[i][1] = a[j][1];
				a[j][1] = temp;
			}
			if (a[i][0] == a[j][0]) {
				a[i][1] = a[i][1] + a[j][1];
				for (int k = j; k < num-1 ; k++) {
					a[k][0] = a[k + 1][0];
					a[k][1] = a[k + 1][1];
				}
				num--;
				j--;
			}
		}
	}
	for (int i = 0; i < num ; i++) {
		cout << a[i][0] << ' ' << a[i][1] << endl;
	}

	return 0;
}

我看网友其他人的答案,发现很多还是用第三题的计数排序的方法来解答的,也通过了测试。其实,这道题并没有规定索引值一定是整数啊!要是有小数,计数排序就没办法了。算是题目的锅吧,同样也附上代码:

#include<iostream>
using namespace std;

int  main()
{
	int Key_Value[10000] = { 0 }, Key, Value, Number, i;
	cin >> Number;
	for (i = 0; i<Number; i++)
	{
		cin >> Key >> Value;
		Key_Value[Key] += Value;
	}
	for (i = 0; i<10000; i++)
	{
		if (Key_Value[i] != 0)
			cout << i << ' ' << Key_Value[i] << endl;
	}

	return 0;
}

9、提取不重复的整数

题目描述:输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。

例如:输入9876673,输出37689。

解答:很容易就想到用计数的方法来进行解决这道题:

#include<iostream>
using namespace std;

int  main()
{
	int num;
	cin >> num;
	int result;
	result = num;
	int a[10][2] = { 0 };
	while (num) {
		a[num % 10][0]++;
		num = num / 10;
	}
	while (result) {
		if (a[result % 10][0] == 1|| (a[result % 10][0] > 1&& a[result % 10][1]==0)) {
			cout << result % 10;
			a[result % 10][1] = 1;
		}
		result = result / 10;
	}

	return 0;
}

但是发现自己是一个很按部就班的人,总是把问题分成很多小目标,然后分别一一解决。但是缺少把几个目标相互关联的本事,就像这道题,可以将两个循环合并来解决:

#include<iostream>
using namespace std;

int main()
{
	int n;
	int a[10] = { 0 };
	int num = 0;
	cin >> n;
	while (n)
	{
		if (a[n % 10] == 0)
		{
			a[n % 10]++;            //这一步是更新,遇到下次相同的数会跳过
			num = num * 10 + n % 10;
		}
		n /= 10;
	}
	cout << num << endl;

	return 0;
}

10、字符个数统计

题目描述:编写一个函数,计算字符串中含有的不同字符的个数。字符在ACSII码范围内(0~127)。不在范围内的不作统计。

例如:输入abc,输出3。

解答:很简单的一个程序,计数法(华为机试题好像很喜欢这个方法,一定要注意):

#include <iostream>
using namespace std;
#include <string>

int main()
{
	string s;
	getline(cin, s);
	int a[128] = { 0 }, num = 0;
	for (int i = 0; i<s.size(); i++) {
		a[(int)s.at(i)]++;
	}
	for (int i = 0; i < 128; i++) {
		if (a[i] != 0)
			num++;
	}
	cout << num;

	return 0;
}

11、数字颠倒

题目描述:输入一个整数,将这个整数以字符串的形式逆序输出。程序不考虑负数的情况,若数字含有0,则逆序形式也含有0,如输入为100,则输出为001。

例如:输入123,输出321。

解答:很简单的一个程序,只不过注意一下用do...while,而不是while:

#include <iostream>
using namespace std;
#include <string>

int main()
{
	int i;
	cin >> i;
	do {
		cout<<(char) (i % 10 + 48);
		i = i / 10;
	} while (i);

	return 0;
}

将数字转化为char的方法:

(char) (i % 10 + 48);
(char) (i % 10 + '0');

常见的ASCII码还是有点印象比较好:

0-9:48-57;A-Z:65-90;a-z:97-122。

12、句子逆序

题目描述:将一个英文语句以单词为单位逆序。所有单词之间用一个空格隔开,语句中除了英文字母外,不再包含其他字符。

例如:输入“I am a boy”,输出“boy a am I”。

解答:单词逆序,但仍然要保持单词。通过substr()函数来进行截取:

#include <iostream>
using namespace std;
#include <string>

int main()
{
	string s, s1, s2;
	int flag = 0, i = 0;
	getline(cin, s);
	for (int j = 0; j < s.size(); j++) {
		if (s.at(j) == ' ') {
			s1 = s.substr(flag, j - flag);
			if (i == 0) {
				s2 = s1;
				i++;
			}
			else
				s2 = s1 + " " + s2;
			flag = j + 1;
		}
		if (j == s.size() - 1) {
			s1 = s.substr(flag, j - flag + 1);
			s2 = s1 + " " + s2;
		}
	}
	cout << s2;

	return 0;
}

参考网上的一些程序,发现一个很巧妙的方法!通过stack“先进后出”的特性:

#include <iostream>
#include <stack>
#include <string>
using namespace std;

int main()
{
	stack<string> ss;
	string s;
	while (cin >> s)
	{
		ss.push(s);
	}
	while (!ss.empty())
	{
		cout << ss.top();
		ss.pop();
		if (!ss.empty())
			cout << ' ';
	}
	cout << endl;

        return 0;
}

13、字串的连接最长路径查找

题目描述:给定n个字符串,请对n个字符串按照字典序排列。

例如:输入9 cap to cat card two too up boat boot,输出boat boot cap card cat to too two up。

解答:直接排序就行了。对于字符串“>、<、==、+”这些运算符都被重载了,可以直接用:

#include <iostream>
using namespace std;
#include <string>

int main()
{
	int num;
	cin >> num;
	string s[1000], temp;
	for (int i = 0; i < num; i++) {
		cin >> s[i];
	}
	for (int i = 0; i < num-1; i++) {
		for (int j = 0; j < num-i-1; j++) {
			if (s[j] > s[j+1]) {
				temp = s[j];
				s[j] = s[j+1];
				s[j+1] = temp;
			}
		}
	}
	for (int i = 0; i < num; i++)
		cout << s[i] << endl;

	return 0;
}

参考其他人的程序,发现一堆使用STL封装的数据结构和函数的……总感觉脱离了出题人的本意了,原本考验算法的,直接一个函数就解决……(可能是因为自己不太熟悉这些函数就酸吧):

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	int input;
	while (cin >> input)
	{
		string str;
		vector<string> vs;
		while (input--)
		{
			cin >> str;
			vs.push_back(str);
		}
		sort(vs.begin(), vs.end());
		vector<string>::iterator vit;
		for (vit = vs.begin(); vit != vs.end(); vit++)
		{
			cout << *vit << endl;
		}
	}

	return 0;
}

14、求int型正整数在内存总存储时1的个数

题目描述:输入一个int型的正整数,计算出该int型数据在内存中存储时1的个数。

例如:输入5,输出2。

解答:这道题可以使用i&(i-1)的方式来求1的个数:

#include <iostream>
using namespace std;

int main()
{
	int i,j=0;
	cin >> i;
	while (i) {
		i=i & (i - 1);
		j++;
	}
	cout << j;

	return 0;
}

当然,看了其他网友的程序,也有其他的方法:

#include<iostream>
using namespace std;

int main()
{
	int a, count;
	cin >> a;

	count = 0;
	while (a>0)
	{
		if (a & 1)
			count++;
		a >>= 1;
	}
	cout << count << endl;

	return 0;
}

或者:

#include<iostream>
using namespace std;

int main()
{
	int t;
	cin >> t;
	int count = 0;
	while (t>0)
	{
		if (t % 2)   count++;
		t = t / 2;
	}
	cout << count << endl;

	return 0;
}

15、坐标移动

题目描述:开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。输入:合法坐标为A(或者D或者W或者S) + 数字(两位以内),坐标之间以“;”分隔。非法坐标点需要进行丢弃。如AA10;  A1A;  $%$;  YAD; 等。

例如:输入A10;S20;W10;D30;X;A1A;B10A11;;A10;,输出10,-10。

解答:这道题还是在华为机试题里第一次遇到的比较复杂的题目。想了蛮久,用了一堆的标志位:

#include <iostream>
using namespace std;
#include <string>

int main()
{
	string s;
	while (getline(cin, s)) {
		int x = 0, y = 0;
		int m = 0, n = -1, k = 0;
		bool flag = false, flag1 = false;
		for (int i = 0; i < s.size(); i++) {
			if (s.at(i) == 'W' || s.at(i) == 'A' || s.at(i) == 'S' || s.at(i) == 'D') {
				if (n != -1) flag1 = true;
				if (s.at(i) == 'W') n = 0;
				if (s.at(i) == 'A') n = 1;
				if (s.at(i) == 'S') n = 2;
				if (s.at(i) == 'D') n = 3;
				if (k == 0) flag = true;
			}
			k++;
			if (flag) {
				if (s.at(i) <= '9'&&s.at(i) >= '0') {
					m = m * 10 + s.at(i) - '0';
				}
			}
			if (s.at(i) == ';') {
				if (flag1) m = 0;
				if (n == 0) y = y + m;
				if (n == 1) x = x - m;
				if (n == 2) y = y - m;
				if (n == 3) x = x + m;
				n = -1;
				m = 0;
				k = 0;
				flag = false;
				flag1 = false;
			}
		}
		cout << x << ',' << y << endl;
	}

	return 0;
}

需要注意的是:我这边采用循环输入的方式:

while (getline(cin, s)) {
        ...
}

如果不采用这种方式的话,会报错,通过不了测试……具体什么时候需要循环输入,什么时候不需要循环输入……不知道……

参考网上的解答,发现了一个不错的程序:

#include<iostream>
#include<string>
#include<cstddef>   //std::size_t
using namespace std;

int main()
{
	string str;
	while (cin >> str) {
		pair<int, int> point(0, 0);               //point.first point.second
		size_t found = str.find_first_of(';');  //找到第一个';'的位置
		int start = 0;

		while (found != string::npos) {
			string s1 = str.substr(start, found - start);
			start = found + 1;
			found = str.find_first_of(';', found + 1);

			if (s1.size()>1 && s1.size() <= 3) {    //合法的字符个数:2或3
				char c = s1[0];
				int n = 0;
				int invalid = 0;    //数字为是否非法
				for (int i = 1; i<s1.size(); ++i) { //数字位判断与提取,A1A
					if (s1[i] >= '0'&&s1[i] <= '9')
						n = n * 10 + (s1[i] - '0');
					else {
						invalid = 1;
						break;
					}
				}
				if (invalid == 0) {
					switch (c)
					{
					case 'A': {point.first -= n; break; }
					case 'D': {point.first += n; break; }
					case 'W': {point.second += n; break; }
					case 'S': {point.second -= n; break; }
					}
				}

			}
		}
		cout << point.first << ',' << point.second << endl;
	}

	return 0;
}

这段程序采用substr()函数来截取字符串的一部分,同时使用find_first_of()函数来寻找字符串当中的字符。需要注意的是,find_first_of()函数的第二个参数为:查找的起始索引位置。

注意:find()函数和find_first_of()函数两者的意义大体相同,它们之间最大的区别就是如果在一个字符串str1中使用find_first_of()来查找另一个字符串str2,如果str1中含有str2中的任何字符,则就会查找成功;而find则不同。

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80943729