目录
第一题:神奇的字母(二)
题目链接:神奇的字母二
题目描述
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
我有个神奇的字母。
但我不告诉你是什么字母。
什么?你想知道这个字母是什么?那你就来猜呀~
字母的范围从'a'到'z'。
我会给你一段话,神奇的字母就是出现次数最多的那个字母哦~
输入描述:
一段话,仅由英文小写字母和空格组成。这段话可能有很多行。(保证存在出现次数最多的一个字母)
数据范围:所有字符串总长度之和不超过1000。
输出描述:
出现次数最多的那个神奇的字母。
输入
ranko sekai ichiban kawaii ranko saikou
输出
a
说明
这段话只有a出现了7次,其他字母都小于7次。
用一个hash数组来统计每个字母出现的次数就可以找到出现次数最多的。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
int ans = 0;
char c = 0;
int hash[26] = { 0 };
while (cin >> s) {
for (auto ch : s) {
hash[ch - 'a']++;
if (hash[ch - 'a'] > ans) {
ans = hash[ch - 'a'];
c = ch;
}
}
}
cout << c << endl;
return 0;
}
第二题: 字符编码
题目链接:字符编码
描述
请设计一个算法,给一个字符串进行二进制编码(哈夫曼编码),使得编码后字符串的长度最短。
数据范围:字符串长度满足 1<𝑛≤1000 ,本题有多组输入
输入描述:
每组数据一行,为待编码的字符串。保证字符串长度小于等于1000。
输出描述:
一行输出最短的编码后长度。
输入:
MT-TECH-TEAM
输出:
33
哈夫曼编码。
统计每个字符出现的次数 ------> 用字符出现的次数(权值)来构建小堆 ------> 从小堆中依次取两个数据,累加求树的带权路径长度 --------> 把累加结果加入到堆中 --------> 堆中只剩一个元素为止。
求树的带权路径长度的方法不唯一,可以通过累加非根节点的权值得到。
#include <iostream>
#include <string>
#include <queue>
using namespace std;
int main()
{
string s;
while (cin >> s) {
int hash[300] = { 0 };
for (auto ch : s) {
hash[ch]++;
}
priority_queue<int, vector<int>, greater<int>> heap;
for (auto val : hash) {
if (val != 0) {
heap.push(val);
}
}
int ans = 0;
while (heap.size() > 1) {
int t1 = heap.top(); heap.pop();
int t2 = heap.top(); heap.pop();
ans += t1 + t2;
heap.push(t1 + t2);
}
cout << ans << endl;
}
return 0;
}
第三题:最少的完全平方数
描述
给定一个正整数n,请找出最少个数的完全平方数,使得这些完全平方数的和等于n。
完全平方指用一个整数乘以自己例如1*1,2*2,3*3等,依此类推。若一个数能表示成某个整数的平方的形式,则称这个数为完全平方数。例如:1,4,9,和16都是完全平方数,但是2,3,5,8,11等等不是
数据范围:1≤𝑛≤10^4
输入描述:
仅一行,输入一个正整数 n
输出描述:
按题目要求输出完全平方数之和为n的最少个数
示例1
输入:
5
输出:
2
说明:
1+4=5
完全背包。
1)状态表示:在前 i 个数中选,和为 j 时所需平方数的最少个数。
2)状态转移方程:
- i 位置的数不选:dp[ i ] [ j ] = dp[ i - 1][ j ] --------①
- i 位置的数选:可以选1个、2个、3个……n个
dp[ i ] [ j ] = min( dp[ i - 1] [ j - i * i ] + 1, dp[ i - 1] [ j - 2 * i * i ] + 2, dp[ i - 1] [ j - 3 * i * i ] + 3……) ---------②
====>>
dp[ i ] [ j ] = min( dp[ i - 1][ j ],dp[ i - 1] [ j - i * i ] + 1, dp[ i - 1] [ j - 2 * i * i ] + 2, dp[ i - 1] [ j - 3 * i * i ] + 3……) ---------③
观察第②个式子,i - 1一直不变,列是有规律的在递减。
在等式③中,令 j = j - i * i ======>>
dp [ i ] [ j - i * i ] = min(dp[ i - 1][ j - i * i ],dp[ i - 1] [ j - 2 * i * i ] + 1, dp[ i - 1] [ j - 3 * i * i ] + 2, dp[ i - 1] [ j - 4 * i * i ] + 3……)
等式两边同时 + 1 ========>>
dp [ i ] [ j - i * i ] + 1 = min(dp[ i - 1][ j - i * i ] + 1,dp[ i - 1] [ j - 2 * i * i ] + 2, dp[ i - 1] [ j - 3 * i * i ] + 3, dp[ i - 1] [ j - 4 * i * i ] + 4……) ---------④
=======>>
式子④可以表示式子②中的诸多情况,所以状态转移方程为:
dp[ i ] [ j ] = min( dp[ i - 1][ j ] ,dp [ i ] [ j - i * i ] + 1);
需要注意的是,j >= i * i。
3)初始化:dp [ 0 ] [ 0 ] = 0,其余的无穷大。
4)填表顺序:上 ----> 下,左 ----> 右
5)返回值:dp[ 根号n] [ n ] -----> 在前根号n(包含)个数中找就可以了,下面的代码实现中,下标从1开始, 在前 i 个数中选,选的不是 i ,而是 i ^ 2.
最后,进行空间优化:把未进行空间优化的代码中的第一维砍掉,即
dp [ j ] = min( dp[ j ] ,dp [ j - i * i ] + 1);
未进行空间优化的代码是过不了的~
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e4 + 10;
const int INF = 0x3f3f3f3f;
int dp[N];
int main()
{
int n;
cin >> n;
memset(dp, INF, sizeof(dp));
dp[0] = 0;
for (int i = 1; i * i <= n; i++) {
for (int j = i * i; j <= n; j++) {
dp[j] = min(dp[j], dp[j - i * i] + 1);
}
}
cout << dp[n] << endl;;
return 0;
}