都是来自牛客网面经,主要侧重于非leetcode题
1.
题目:2n个数的数组,[1,1,2,2,…,n,n],输出所有可能的数组集合,使前面非递减后面非递增
首先一种暴力的做法,从中任选n个,排序,总能满足要求,只是太慢了。
假如我们考虑n个数的成分,比如有几个1,几个2,....几个n,只要满足个数和为n,这样都不用排序了
dfs写一下就好了,也可以写个三进制枚举
而且我们还可以直接求得个数,使用组合数学,每个数字有0,1,2三种个数,要求总个数为n,因此方案数是 \((1+x+x^2)^n\) 的 \(x^n\) 项的系数
int cnt = 0; // (1+x+x^2)^n 的x^n项的系数,也是最大系数
// 枚举到第i个,sum是 前面个数和,
void dfs(int n, int i, vector<int>& path, int sum) {
// cout << "i: " << i << " " << sum << endl;
if(i == n ) {
if(sum == n) {
for(int j = 0; j < n; j++) {
for(int k = 0; k < path[j]; k++) {
cout << j+1;
}
}
cnt++;
cout << endl;
}
return;
}
for(int j = 0; j <= 2; j++) {
if(sum+j > n) break;
path[i] = j;
dfs(n, i + 1, path, sum+j);
// path[i] = 0;
}
return;
}
2.
题目:现在有一部电梯,在某一楼层N,你只可以乘坐这个电梯向上或者向下K层(K是N的所有因子), 给定当前楼层i和目标楼层j,求最少需要乘坐多少次电梯从当前楼层到达目标楼层。
第一眼思路是记忆化搜索,然后写完发现,会出现相互依赖进入死循环。
反过来想,从目标层j出发,dp[j]=0,然后更新一步之内能到达的并加入队列,再拿这一批去更新....
按理说可以一层一层更新,为了编码方便,我使用的优先队列,能次取最小的出来更新
这种贪心的正确性 和 Dijsktra算法类似
int short_elevator(int n, int i, int j) {
vector<int> factors = factor(n); // 求N的所有因子
vector<int> dp(n, -1);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.push(make_pair(0, j));
dp[j] = 0;
while(!pq.empty()) {
int val = pq.top().first;
int cur = pq.top().second;
cout << "val: " << val << " cur: " << cur << endl;
pq.pop();
if(cur == i) return pq.top().first;
for(int k = 0; k < factors.size(); k++) {
int next = cur + factors[k];
if(next <= n && dp[next] == -1) pq.push(make_pair(val+1, next));
next = cur - factors[k];
if(next >= 1 && dp[next] == -1) pq.push(make_pair(val+1, next));
}
}
return -1; // 到不了为-1,因为因子存在1,肯定能到
}
害,就是最短路,能互达的两层之间连边,求i到j的最短路,但是边权都是1;
由于边权为1,前面更新的一定是最短路,不会被再次更新,因此可以用dp[next] == -1
判断;
由于边权为1,直接BFS就行...啊这这都没看出来,菜哭,都不用上优先队列;
我这写的啥,BFS和Dijkstra混杂??
3.
题目:1. 给定一个源字符串a,一个目标字符串b,一个替代字符串c。将a中的b更换为c。例:a="Hello, world", b=" ", c="%2b",则替换后a变为"Hello,%2bworld"
- 最优解应该用KMP算法 ,我现场用了暴力双指针。
- 面试官说考这道题的意义是数组扩容什么的,我没懂……
思路:数组填充题,先扩容在移动. i指向新长度的末尾,j指向旧长度的末尾。
其实这是leetcode 剑指 Offer 05. 替换空格
参考 替换空格题解
class Solution {
public:
string replaceSpace(string s) {
int n = s.size();
int cnt = 0; // 统计空格的个数
for(int i = 0; i < n; i++) {
if(s[i] == ' ') cnt++;
}
s.resize(n+cnt*2);
int j = n+cnt*2-1;
for(int i = n-1; i >= 0; i--) {
if(s[i] == ' ') {
s[j--] = '0';
s[j--] = '2';
s[j--] = '%';
// cnt++;
} else {
s[j--] = s[i];
}
}
return s;
}
};
4.
题目:
00001.最大子数组和(LeetCode原题,n时间1空间)
- 两个长度为m的无序数组A,B,对于任意不相交的区间ab和cd,val[ab]=sum(A,a,b)- sum(B,a,b),val[cd] = sum(B,c,d)- sum(A,c, d)
求abcd,使val[ab] + val[cd]最大 (这题比较难,先写了个暴力解法,然后和面试官逐步讨论优化,没有给出最优解法)
第一题很简单,dp[i] 表示以i结尾的最大子数组,省掉数组可以用一个变量表示
- 最大子数组和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size() == 0) return 0;
int sum = 0, ans = nums[0];
for(int i = 0;i < nums.size();i++) {
sum = (sum<0 ? 0 : sum) + nums[i]; // 条件表达式记得加括号
ans = max(ans, sum);
}
return ans;
}
};
第二题,看起来比较难,既然有两小份,是不是暗示了什么??
假设[a,b] 与 [c,d] 从i 分开,val[ab] + val[cd]的最大值就是val[ab]在i之前找最大值,val[cd]在i之后找最大值,
val[ab]=sum(A,a,b)- sum(B,a,b),如果我们将A对应的元素减去B,不就是新数组的最大子数组问题了嘛
同理val[cd] 可以用B减A