开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 25 天,点击查看活动详情
Day02 2023/02/28
难度:简单
题目1
编写一个函数,计算字符串中含有的不同字符的个数。字符在ASCI码范围内(0~127,包括0和127),换行表示结束符,不算在字符里。不在范围内的不作统计。多个相同的字符只计算一次例如,对于字符串abaca而言,有a、b、c三种不同的字符,因此输出3。数据范围:1<=n<=500
输入描述 输入一行没有空格的字符串。 输出描述: 输入字符串中范围在(0~127,包括0和127)字符的种数。输入:
题目2
从键盘输入两个数字n,m,求解n和m的最小公倍数。
示例1
输入: abseef
输出: 5
说明: 去重后的字符个数为5
示例2
输入: 35 14
输出: 70
运行实例
思路一
使用辅助数组记录每种字符是否已经出现
根据题目要求字符在 ASCII 码范围内( 0~127 ,包括 0 和 127 )。呢我们只需要维护一个大小为128的数组用来比较字符,让输入的字符作为数组下标,取出这个下标中辅助数组中的字符直接与输入的字符比较,如果相同则跳过,不同则赋值,让计数器+1,最后返回计数器即可。
思路二
求两数的最小公倍数,我们可以使用这个公式,两数的乘积等于两数的小公倍数*两数的最大公约数,所以本题转化成了如何求最大公约数,那方法就多了,比如辗转相除,辗转相减,分解质因数法,这里我们采用最常见的辗转相除法即可。
辗转相除法简单复习:
- 假如需要求35 和 14 两个正整数的最大公约数,用欧几里得算法(辗转相除法),是这样进行的:
- 35%14(余7)
- 14%7 (余0)
- 至此,最大公约数为14,以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数
关键点
- 思路一中要理解所有的字符都可以转化成0~127范围内的ASCII码,所以自然可以作为数组下标使用,并且初始辅助数组中元素值都为空值。
- 思路二中求最大公约数函数的代码书写中,还涉及到了递归的写法,只要是把一个复杂问题转化成若干个简单的相同问题,就可以使用递归的写法。
算法实现
c++代码实现
#include <iostream>
#include <string>
using namespace std;
//获取字符串中去重后的字符个数
int GetNumOfCharacters(string str) {
int count = 0; // 计数器
char asc[128]; // 辅助数组,用来记录当前字符是否已经出现过,初始每个元素为空
for (auto it : str) { // 遍历字符串
if (asc[int(it)] != it) { // 和辅助数组进行比较,不相等(表明当前字符是首次出现),否则直接跳过(表明是重复字符)
asc[int(it)] = it; // 将首次出现的字符记录到辅助数组中
count++; // 计数器加1
}
}
return count;
}
// 求n和m的最大公倍数
int GetMaxCommonDivisor(int n, int m) {
if (n % m == 0) return m;
else return GetMaxCommonDivisor(m, (n % m)); // 辗转相除的方式
}
// 求n和m的最小公倍数
int GetMinCommonMultiple(int n, int m) {
return (n * m) / GetMaxCommonDivisor(n, m); // 利用公式法:即两数的乘积除以两数的最大公约数 = 最小公倍数
}
int main() {
// 测试题目一:
string str;
cin >> str;
int res1 = GetNumOfCharacters(str);
cout << "字符串中不同字符的个数为:" << res1 << endl;
// 测试题目二
int n, m;
cin >> n >> m;
int res2 = GetMinCommonMultiple(n, m);
cout << n << "和" << m << "的最小公倍数为:" << res2;
}
求字符串不同字符个数:
- 时间复杂度 --- 遍历整个字符串,其中n为字符串长度
- 空间复杂度 --- 都是常数级的辅助变量,除此没有额外的辅助空间
求n和m的最小公倍数
- 时间复杂度 --- 循环次数取决于两数相除的次数,但小于m和n,其中t为两数相除的次数
- 空间复杂度 --- 递归栈的深度为t,其中t为两数相除的次数
总结
- 我们在求空间复杂度的时候固定大小的数组我们时视作常数级的变量,其次不要忽略递归栈所占的空间大小也要记入空间复杂度中。