华科历年机试题——难点,生僻知识点

2021-2

输入一个占32 3232比特位的十进制正整数,按下述规则输出加密后的十进制整数:
设这个整数的二进制表示为

x x x x p p p p x x x x e e e e x x x x e e e e x x x x e e e e

,将其中标记为eeee的均左移8位,标记为pppp的右移24位,标记为xxxx的位置不变,输出加密后的数的十进制表示

Sample Input:
1234
Sample Output:
262864

题解:将x, p, e分解出来,然后再分别将p左移,e右移之后再加上x。

#include
int main()
{
	long long n;
	while(scanf("%lld", &n) != EOF)
	{
		long long e = n & (0x000f0f0f);
		long long p = n & (0x0f000000);
		long long x = n-e-p;
		long long res = (e << 8) + (p >> 24) + x;
		printf("%lld", res);
	}
}

<< x表示对二进制数左移x位, >> x表示右移x位(均为逻辑移位)
&表示与, |表示或, ~表示取反, ^表示异或
这些运算符均可后面直接加整型数字

2021-3

输入两个整数a和n,要求从a中删除n个位置上的数后剩下的数按原来的相对顺序组成的新数最小,求这个数

Sample Input:
62354 2
Sample Output:
234

解题思路:先定义一个结构体来存放a中每个位置上的字符已经该字符的位置,然后对字符进行排序,这样字符中最大的n个数字就会排在末尾,截取前面的部分(相当于删除了n个最大数字),再对前面几个数字根据刚开始记录的位置进行排序,这样前面的数字就能回归原来的顺序。

#include
#include
#include
#include
using namespace std;
struct f{
	char c;
	int pos;
}s[10];
bool cmp1(f a, f b)
{
	return a.c < b.c;
}
bool cmp2(f a, f b)
{
	return a.pos < b.pos;
}
int main()
{
	string a;
	int n;
	while(cin >> a >> n)
	{
		for(int i = 0; i < a.length(); i++)
		{
			s[i].c = a[i];
			s[i].pos = i;
		}
		sort(s, s+a.length(), cmp1);
		sort(s, s+a.length()-n, cmp2);
		for(int j = 0; j < a.length()-n; j++)
		printf("%c", s[j].c);
		printf("\n");
	}
}

这个解法昨晚睡觉时想起来发现时错的,例如96328,去掉两位,按这个做法时632,但最小应该是328。
所以看了别人的解题思路华科2021机试:
96328去除两位还剩3位。设去除后的结果为result,则result第一位在963中选择(即【1,3】中选),不然会导致接下来的数字不够再选两位,而剩下两位只能从位置4开始选。
所以

转自上文链接

#include
#include
#include
using namespace std;

char s[1005];
int min_pos[1005][1005]; // min_pos[i][j]表示区间[i,j]的最小数的位置,如果有多个最小数,那么取最左边的那个数

int main() {
	int m,n;
	while(~scanf("%s %d",s+1,&m)) {//从s[1]开始存储
		n = strlen(s+1);
		for(int i=1;i<=n;i++) {
			min_pos[i][i] = i;
			for(int j=i+1;j<=n;j++) {
				min_pos[i][j] = s[j] < s[min_pos[i][j-1]] ? j : min_pos[i][j-1];//依次找每个范围内的最小值的位置,用到了动态规划
			}
		}
		int pre_pos = 0;
		for(int i=1;i<=n-m;i++) printf("%c",s[pre_pos = min_pos[pre_pos+1][m+i]]);//pre_pos表示上一个最小数字的位置,下一个数字从pre_pos下一个来找
		printf("\n");
	}
}

2019-1

  • double fabs(double x)
    函数原型: double fabs(double x)
    函数功能: 返回 x 的绝对值。
  • double sqrt(double x)
    函数原型: double sqrt(double x)
    函数功能: 该函数返回 x 的平方根
  • double pow(double x, double y)
    函数原型: double pow(double x, double y)
    函数功能: 返回 x 的 y 次幂,即 x的y平方
  • 判断素数的模板
bool judge(int x)
{
	if(x <= 1) return false;
	for(int i = 2; i*i <= x; i++)
	if(x % i == 0)
	return false;
	
	return true;
 } 

题目:1.关于质数的问题:若x为质数,且2^x-1也为质数,则称x为梅森数,例如:3为质数,2 ^3-1=7也为质数,所以3满足条件,再如11是质数,但2 ^11-1=2047不是质数,所以11不满足条件;题目要求输入一个整数m,求出小于m满足条件的吗,梅森数,例如:
输入:97
输出:
M(2)=3
M(3)=7
M(5)=31

#include
#include
bool judge(int x)
{
	if(x <= 1) return false;
	for(int i = 2; i*i <= x; i++)
	if(x % i == 0)
	return false;
	
	return true;
 } 
int main()
{
 	int n;
 	while(~scanf("%d", &n))
 	{
 		for(int i = 2; i < n; i++)//ksdhflkdsghldfglksgd 
 		{
 			int res = (int)pow(2.0, double(i))-1;/*erskghrlktghlrtgnklddfbsgf 
			 dhsfthhfdtrh
			 rthtyhty*/ 
 			if(res < n && judge(i) && judge(res))
 			printf("M(%d)=%d\n", i, res);
		 }
	 }
} 

2019-2

打开第一题的代码源文件.c/ .cpp要求原文件里含有//和/ * XXXXXX * /,输出该文件,共有三小问:
1.给每行代码加上序号,表明这是第几行
2.对于//字符不予输出//及该行之后的文本
3.同上,要求不要输出/ * * /及其之内的文本

解题思路:对原文件一行一行的读取,然后每一行一个字符一个字符的检测,如果出现//,那么忽略后面的内容,直接换行;如果出现/* */一般这类的注释都会有好几行,这个时候当遇到/ * 时对zs变量设置为1,表示现在遇到了多行注释,当遇到 * /时表示整行注释已经结束,设置zs为0。在一个字符一个字符的检查过程中若zs=0且没有遇到//或/ *则输出字符。

#include
#include
int main()
{
	FILE * fp = fopen("2019-1.cpp", "r");
	char s[100], ch;
	int cnt = 1, zs = 0;
	while(fgets(s, 200, fp) != NULL)
	{
		int len = strlen(s);
		if(zs == 0)
		printf("%d ", cnt++);
		for(int i = 0; i < len; i++)
		{
			if(s[i] == '/')
			{
				if(i+1 < len && s[i+1] == '/')
				{
					printf("\n");
					break;
				}
				else if(i+1 < len && s[i+1] == '*')
				{
					zs = 1;
					break;
				}
				else if(zs == 0)
				printf("%c", s[i]);
			}
			else if(s[i] == '*' && i+1 < len && s[i+1] == '/')
			zs = 0;//在原文件中*/后面是换行符 
			else if(zs == 0)
			printf("%c", s[i]);
		}
	}
	
 } 

输出:
华科历年机试题——难点,生僻知识点_第1张图片
关于c语言对文件的操作:参考博客

2018-3

接受3个以N/D的形式输入的分数,其中N为分子,D为分母,均在范围(0,65535)之间,输出他们的小数形式。如果小数存在循环节,则用括号括起来,如1/3=.33333…表示为0.(3)
例如:

输入:8/5 1/3 11/13
输出:
8/5=1.6
1/3=.(3)
11/13=.(846153)

解题思想:手动模拟除法过程。

  1. 首先N/D,先处理整数部分,若得到的商p不为0,则加入结果的字符串res中,如果为0,则省略。同时记录用r记录N/D的余数。
  2. res直接加上小数点。
  3. 再来处理小数部分。

如1/3,第一步p = 0,r = 1;
然后将余数r*10与3相除,得到商p = 3,余数为1
再来一次刚刚的步骤,p=3,r=1

可以看到当开始出现循环的时候,p对应的r的值是与之前一样的,这种关系在程序中可以用一个map实现对应关系。

同时要记录p的位置方便插入‘( ’,原因是前面的可能不是循环节如17/22 = 0.7(72)。

#include
#include
#include
using namespace std;
map<int, int> mp, pos;
string getr(int a, int b)
{
	string res = "", f = "";//f存储小数部分
	
	int p = a/b, r = a % b, rotate = 0, cnt = 0;//rotate记录是否找到循环节,cnt记录每一个小数所在位置
	int index;//记录循环节的第一个数
	
	if(p != 0)
	res += to_string(p);//处理整数部分 
	
	res += '.';
	while(r != 0)
	{
		p = r * 10 / b;
		r = r * 10 % b;
		if(r != 0 && mp[p] == r)//如果商与余数之前已经对应了,说明已经找到循环节
		{
			rotate = 1;//表示循环节已经找到
			index = p;
			break;
		}
		f += (p+'0');//用f保存小数部分 
		mp[p] = r;//记录商对应的余数
		pos[p] = cnt++;//记录p在小数部分出现的位置,若p为循环节第一个,则(可以插入p在小数部分的位置
	}
	if(rotate == 1)
	{
		string c = "(";
		f.insert(pos[index], c);
		f += ')';
	}
	res += f;
	
	return res;
}
int main()
{
	int a, b;
	while(~scanf("%d/%d", &a, &b))
	{
		mp.clear();
		string res = getr(a, b);
		printf("%s\n", res.c_str());
	}
	return 0;
 } 

测试样例

1/3
.(3)
11/13
.(846153)
17/22
.7(72)
8/5
1.6
7/3
2.(3)

2015-3

题目:

无冗余输出一个字符串,对于不是首次出现的字符,对其进行过滤。如abcdacdef,过滤后为abcdef。对于字符0-9,a-f,A-F,将其对应的ASCII码低4位进行对调,例如将1101转换为1011,并将对应的ASCII码的字符进行输出,若为字母,转换为大写。

思路:对于ASCII码低四位颠倒,可以先取出低四位,用原数a与0fH进行与得到的结果给f,然后a减去f,这样a的低四位就为0。然后用一个循环去f的四位二进制,求出来的四位二进制与原来的位置相反,正好满足题目要求。再将求出来的二进制转化为十进制,然后将所得十进制加上a即可。

#include
#include
#include
#include
using namespace std;
int app[300];
int change(char a)
{
	int f = a & (0x0f), total = 0;
	a -= f;//清空低四位
	
	string res = "";
	while(f != 0)
	{
		int r = f % 2;
		f /= 2;
		res += (r+'0');
	}//求二进制并加入res字符串中,二进制与原来的低四位相反
	
	while(res.size() < 4)
	res += '0';//求出来的二进制不足四位则低位补0
	
	for(auto x : res)
	total = total * 2 + (x-'0');//将求出的二进制转化为10进制
	
	total += a;//加上原来的数后,原来的数的低四位就完成了倒置
	return total;
}
int main()
{
	string a;
	while(cin >> a)
	{
		cout << a << endl;
		memset(app, 0, sizeof(app));
		string res = "";
		for(auto i:a)
		{
			if(app[i] == 0)
			{
				cout << i;
				app[i] = 1;
				
				if((i >= '0' && i <= '9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F'))
				{
					char num = change(i);
					if(num >= 'a')
					num = num-'a'+'A';//若为小写字母,则转化为大写,小写字母ascii码大于大写字母
					
					res += num;
				}
			}
		}
		cout << endl << res << endl;
	}
}

2013-3

题目:

输入一个字符串,求出其中最长的回文子串。子串的含义是:在原串连续出现的字符串片段。回文的含义是:正着看和倒着看是相同的,如abba和abbebba。在判断是要求忽略所有的标点和空格,且忽略大小写,但输出时按原样输出(首尾不要输出多余的字符串)。输入字符串长度大于等于1小于等于5000

思路:这题求最长回文子串可以用动态规划来求,关键是如何处理标点符号。可以先将字符串中的空格、标点符号去除保存在res中,找到res的最长回文子串后再与原字符串比较,当比较的原字符串中的字符为标点或空格时就跳过,直到比完,说明此时找到了该字符串在原串中的位置。
最长回文子串(动态规划):参考博客链接

#include
#include
#include
#include
using namespace std;
bool is_char(char a)
{
	if((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || (a >= '0' && a <= '9'))
	return true;
	else return false;
}//判断是否不是标点或空格
void find_pos(string s, string res, int &start, int &finish)
{
	int index = 0;
	for(int i = 0; i < s.length(); i++)
	{
		if(!is_char(s[i]))
		continue;//原串中的字符是标点或空格则跳过
		if(s[i] == res[index] || abs(s[i]-res[index]) == 32)
		{//如果相等或一个为大写字母一个为小写字母
			if(index == 0)
			start = i;//找到最长回文子串在原串的起始位
			
			index++;
			if(index == res.length())
			{
				finish = i;
				break;
			}//如果比较到了最长回文子串末尾,则找到了
		}
		else
		{
			if(index != 0)
			i--;//如果比较的是第一个字符,不相等则原串与子串均下移一位,如果比较的是第n个字符且不等,则子串从头开始比较,此时原串的位置不变。这里减一是为了与循环体的i++抵消。
			
			index = 0;
		}
	}
}
int dp[5010][5010];
int main()
{
	string s;
	while(getline(cin, s))
	{
		string res="";
		memset(dp, 0, sizeof(dp));
		for(auto i:s)
		{
			if(is_char(i))
			{
				if(i >= 'A' && i <= 'Z')
				i = i-'A'+'a';
				res += i;
			}//将所有大写字母转化为小写,忽略标点符号和空格
		}
		
		int len = res.length(), maxlen = 1, start = 0;//start为res中最长回文子串的起始位置
		for(int i = 0; i < len; i++)
		{
			dp[i][i] = 1;
			if(i+1 < len && res[i] == res[i+1])
			{
				dp[i][i+1] = 1;
				maxlen = 2;
				start = i;
			}
		}
		for(int L = 3; L <= len; L++)
		{
			for(int i = 0; i+L-1 < len; i++)
			{
				int j = i+L-1;
				if(res[i] == res[j] && dp[i+1][j-1] == 1)
				{
					dp[i][j] = 1;
					maxlen = L;
					start = i;
				}
			}
		}
		int left = 0, right = 0;//left,right分别为原串最长回文子串的起始位与结束位
		find_pos(s, res.substr(start, maxlen), left, right);
		cout << s.substr(left, right-left+1) << endl;
	}
 } 

你可能感兴趣的:(leetcode题解,算法,c++)