微软面试算法题(整理)

都是来自牛客网面经,主要侧重于非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空间)

  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结尾的最大子数组,省掉数组可以用一个变量表示

  1. 最大子数组和
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

posted @ 2021-12-19 15:47  Rogn  阅读(427)  评论(0编辑  收藏  举报