京东9-3笔试

投递岗位C++
20道选择,体感C++基础考的多,考了类、多态和派生,外加一些经典八股,数据结构,计网的握手挥手、读数据库代码等,同步异步和linux。
3道编程,有两道都是dp。
1.求残次品的个数
输入一个n,表示第二行输入的数量。第二行依次输入的n个产品的质量,已知残次品比正品轻。
这个代码就不写了,统计最大值的个数就行了。输入完成结果就跟着出来了。
2.求最小操作数
题目给出两种操作,一种是减去1,另一种是拆成两个因子。求把一个数字操作成1的最小操作数 ,拆分后两个因子都要到1 。
输入一串数,每个数都要操作,最后返回总和。
思路:接收输入的时候统计一下出现的最大数字maxv,然后计算从0到maxv的每个数的最小操作数,结果保存在dp数组里。计算过程只有两个方案,要么-1,要么直接拆。
贴一下核心代码,当时急着过,肯定不是最优代码,只保证能ac。

3. 求括号字符串子数组的权值和
题目定义了权值,所谓权值是一个括号串里最长有效序列的长度。比如"()(()"的权值是4,因为序列不要求连续,取首尾两对括号"()(()"即可构成一个长为4的有效序列。
现在要求字符串所有子数组的权值之和。
先上暴力,写完暴力就会意识到哪些工作是重复的,暴力只能过22%,因为时间复杂度是O(n³)
int check(string &s,int left,int right){//计算权值
    stack<bool>l;
    int cnt=0;
    for(int i=left;i<=right;++i){//O(n)遍历统计有效括号对
        if(s[i]=='(')
            l.push(1);
        else if(!l.empty()){
            l.pop();
            ++cnt;
        }
    }
    return cnt*2;
}
int test2(string &s){//接收字符串,调用check
    int ans=0;
    int len=s.size();
    for(int i=0;i<len-1;++i)//平方级的枚举子数组
        for(int j=1;j<len;++j)
            ans+=check(s,i,j);
    return ans;
}
再上二维dp,定义dp[i][j] i到j的有效括号个数,还要额外统计此时的左括号是否有余,我直接塞进pair里了
vector<vector<pair<int,int>>>dp
(len,vector<pair<int,int>>(len,make_pair(0,0)));
//i到j的有效括号个数为first,多出来的左括号个数为second,不为负
int res=0;
for (int i = 0; i < len;++i)
    dp[i][i].second= s[i] == '(';
for (int i = 0; i < len - 1; ++i)
    for (int j = i + 1; j < len; ++j){
        if (s[j] == '(')//遇见(  计数+1
        {
            dp[i][j].first = dp[i][j - 1].first;
            dp[i][j].second = dp[i][j - 1].second + 1;
        }
        else if (dp[i][j - 1].second > 0)//遇见)有左括号,长度+2 计数-1
        {
            dp[i][j].first = dp[i][j - 1].first + 2;
            dp[i][j].second = dp[i][j - 1].second - 1;
        }
        else
            dp[i][j] = dp[i][j - 1];//没有左括号,直接沿用上次的值
        res += dp[i][j].first;//记得统计结果,题目要求所有子数组的权值
    }
可以随机一个括号串和暴力算法比对一下,题目的范围是200000的长度,实际上暴力解法1000的长度就已经超过给定用时了,能过22%已经算运气不错了。

但是很遗憾,依然不能全过,虽然我们写了一个看起来很好理解也很美观的DP,因为这个二维DP依然是n²级别的时间复杂度,不可能过得了200000长度的数据。当然能写出来这个已经可以捞不少分了。问题在于,我们只是优化了统计括号对这个操作,但依然要枚举每个子数组,所以下一步优化的结果是一步到位,直接把dp数组定义为 dp[i]: 以i结尾的所有子数组的权值和 。那么dp[i+1]的取值只有两种可能:
  • 一种是没有诞生新的括号对,比如当前是左括号,或者是右括号但左括号用尽。那所有子数组的权值和不变,沿用之前的结果,dp[i+1]=dp[i]
  • 新增括号对的情况。还是用栈记录没有配对的左括号下标,栈顶k就是离当前下标最近的左括号。那么[k...i+1]这个子数组权值要加2,且k之前的所有子数组权值和都要+2,dp[i+1]=dp[i]+2*(k+1)
更多细节参见代码,比如在1000长度的时候答案就已经快超过int范围了,所以必须改用long long
    stack<int> st;
    string s{};
    // cin >> str;
    s = ")()()";
    int len = s.size();
    using ll = long long;
    vector<ll> dp(len + 1,0);
    //dp[i] 以i结尾的所有子数组的权值和
    ll ans = 0;
    for(int i = 1; i <= len; i++){
        dp[i] = dp[i - 1];
        if(s[i - 1] == '(')
            st.push(i-1);//记录左括号下标       
        else{
            if(!st.empty()){//有左括号就配对
                int cnt = st.top()+1;//栈顶是离i最近的第一个左括号的下标
                //0到i,总共i+1个子数组都多了一对括号,权值都要加2
                st.pop();
                dp[i] = dp[i] + cnt*2;
            }
        }
        ans = ans + dp[i];//累加
    }
#京东笔试#
全部评论
给楼主点赞!第三题死活没看懂题目,看了楼主的代码+解释理解了,在这里借楼帮大家快速理解一下题意。题目要求输出字符串的所有子串的权值和,比如输入字符串为()) 那么他的子串有(, (), ()), ), )) 这五种情况,但是并不是所有子串都有权值-即合法的括号存在,这里只有第二和第三种有合法括号存在,权值为合法括号的个数*2,即这里所有子串的权值分别为0,2,2,0,0 再求和即可。
1 回复 分享
发布于 2022-09-04 10:25 重庆
hi~同学,秋招遇“寒气”,牛客送温暖啦!23届秋招笔面经有奖征集中,参与就得牛客会员7天免费体验,最高赢300元京东卡!戳我去看>>>https://www.nowcoder.com/link/zhengjipinglun
1 回复 分享
发布于 2022-09-04 15:26 北京

相关推荐

评论
8
10
分享
牛客网
牛客企业服务