牛客网刷题记录

1.大数相乘(hard)

题目描述

有两个用字符串表示的非常大的大整数,算出他们的乘积,也是用字符串表示。不能用系统自带的大整数类型。

输入描述:

空格分隔的两个字符串,代表输入的两个大整数

输出描述:

输入的乘积,用字符串表示

示例1

输入  72106547548473106236 982161082972751393

输出  70820244829634538040848656466105986748

思路:

我一开始写的很复杂而且没有头绪,下面是别人的解法

使用一个string全为0的string作为结果记录,去更新这个string

#include 
#include 
using namespace std;

string getMutiply(string s1,string s2){
    if(s1.empty() || s2.empty()) return "";
    string res(s1.size()+s2.size(),'0');
    for(int i = s2.size()-1;i >= 0;i--){
        int carry = 0,cur;
        for(int j = s1.size()-1;j >= 0;j--){
            cur = res[i+j+1]-'0' + (s1[j]-'0')*(s2[i]-'0') + carry;
            res[i+j+1] = cur%10+'0';
            carry = cur / 10;
        }
        res[i] += carry;
    }
    int idx = 0;
    while(idx < res.size() && res[idx] == '0') idx++;
    return idx == res.size()? "0":res.substr(idx);
}

int main(){
    string s1,s2;
    cin >> s1 >> s2;
    string res = getMutiply(s1,s2);
    cout <<  res << endl;
    return 0;
}

 

2.lucky string(easy)

A string s is LUCKY if and only if the number of different characters in s is a fibonacci number. Given a string consisting of only lower case letters , output all its lucky non-empty substrings in lexicographical order. Same substrings should be printed once.

输入描述:

a string consisting no more than 100 lower case letters.

输出描述:

output the lucky substrings in lexicographical order.one per line. Same substrings should be printed once.

示例1

输入

aabcd

输出

a 
aa 
aab 
aabc 
ab 
abc 
b 
bc 
bcd 
c 
cd 
d

思路:我一直没法AC,后来发现原因是:

(1)这个不能有重复的输出,所以我取了个set做,否则aab会有两次a输出。

(2)字典序输出,必须sort一下

题目这么看的话其实挺智障的。。。

#include 
#include 
#include 
#include 
#include 
using namespace std;

int main(){
    string s;
    int fib[7] = {1,2,3,5,8,13,21};
    while(cin >> s){ 
        set res;
        for(int i = 0;i < s.size();i++){
            set word_map;
            int k = 0;
            for(int j = i;j < s.size();j++){
                word_map.insert(s[j]);
                if(word_map.size() > fib[k] && k < 6) k++;
                if(word_map.size() == fib[k]) res.insert(s.substr(i,j-i+1));
            }
        }
        for(auto &str:res) cout << str << endl;
    }
}

 

3.简单错误记录(输入输出)(hard)

题目描述

开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号。 
处理:
1.记录最多8条错误记录,对相同的错误记录(即文件名称和行号完全匹配)只记录一条,错误计数增加;(文件所在的目录不同,文件名和行号相同也要合并)
2.超过16个字符的文件名称,只记录文件的最后有效16个字符;(如果文件名不同,而只是文件名的后16个字符和行号相同,也不要合并)
3.输入的文件可能带路径,记录文件名称不能带路径

输入描述:

一行或多行字符串。每行包括带路径文件名称,行号,以空格隔开。

    文件路径为windows格式

    如:E:\V1R2\product\fpgadrive.c 1325

输出描述:

将所有的记录统计并将结果输出,格式:文件名代码行数数目,一个空格隔开,如: fpgadrive.c 1325 1 

    结果根据数目从多到少排序,数目相同的情况下,按照输入第一次出现顺序排序。

    如果超过8条记录,则只输出前8条记录.

    如果文件名的长度超过16个字符,则只输出后16个字符

示例1

输入

E:\V1R2\product\fpgadrive.c 1325

输出

fpgadrive.c 1325 1

思路:这题看着简单,写起来内心一万个卧槽还写不对。。。我写输入输出真的太弱了,后期注意看一下

python:

import sys

files = []
count = []

while True:
    try:
        a = input()
        file_line = a.split('\\')[-1]
        if file_line not in files:
            files.append(file_line)
            count.append(1)
        else:
            idx = files.index(file_line)
            count[idx] += 1
    except:
        break

lenth = min(len(files),8)
sort_count = sorted(count,reverse=True)
for cnt in sort_count[:lenth]:
    idx = count.index(cnt)
    result = files[idx]
    result = result.split()
    if len(result[0])>16:
        print(result[0][-16:]+' '+result[1]+' '+str(cnt))
    else:
        print(result[0]+' '+result[1]+' '+str(cnt))
    del count[idx]
    del files[idx]

 

合唱团(mid)

题目描述

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

输入描述:

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:

输出一行表示最大的乘积。

示例1

输入

3
7 4 7
2 50

输出

49

思路:这题其实是LeetCode原题的变型,思路也差不多,一个难点就是输入有正有负,需要维护一个正数最大值和一个负数最小值

#include 
#include 
#include 
using namespace std;

long long maxProduct(vector stu,int n,int k,int d){
    long long max_res = 1;
    vector> max_dp(k,vector(n,0));
    vector> min_dp(k,vector(n,0));
    for(int i = 0;i < n;i++){
        if(stu[i] == 0) continue;
        max_dp[0][i] = stu[i];
        min_dp[0][i] = stu[i];
        for(int j = 1;j < k;j++){
            long long max_pre = 0,min_pre = 0;
            for(int m = i-1;m >= 0 && m >= i-d;m--){
                max_pre = max(max_pre,max_dp[j-1][m]);
                min_pre = min(min_pre,min_dp[j-1][m]);
            }
            max_dp[j][i] = max(max_pre * stu[i],min_pre * stu[i]);
            min_dp[j][i] = min(max_pre * stu[i],min_pre * stu[i]);
        }
        max_res = max(max_res, max_dp[k-1][i]);
    }
    return max_res;
}

int main(){
    int n,k,d;
    while(cin >> n){
        vector stu(n);
        for(int i = 0;i < n;i++) cin >> stu[i];
        cin >> k >> d;
        long long res = maxProduct(stu,n,k,d);
        cout << res << endl;
    }
}

 

分田地(hard!)

题目描述

牛牛和 15 个朋友来玩打土豪分田地的游戏,牛牛决定让你来分田地,地主的田地可以看成是一个矩形,每个位置有一个价值。分割田地的方法是横竖各切三刀,分成 16 份,作为领导干部,牛牛总是会选择其中总价值最小的一份田地, 作为牛牛最好的朋友,你希望牛牛取得的田地的价值和尽可能大,你知道这个值最大可以是多少吗?

输入描述:

每个输入包含 1 个测试用例。每个测试用例的第一行包含两个整数 n 和 m(1 <= n, m <= 75),表示田地的大小,接下来的 n 行,每行包含 m 个 0-9 之间的数字,表示每块位置的价值。

输出描述:

输出一行表示牛牛所能取得的最大的价值。

示例1

输入

4 4
3332
3233
3332
2323

输出

2

题意:即找到最小值的最大值

思路:这题我开始以为是dp,结果无处下手,这是一道很巧妙的贪心+二分查找

横竖各三刀将一个矩阵分为16部分,每部分的value即为这一部分包含的所有数字之和。

我们要做的就是想一种切法,使得这16部分中value最小的那个尽可能的大。

首先很显然,每一个部分的value在0-sum之间,sum是指整个矩阵所有数字之和,

这样最终的结果一定是[0, sum]中的某一个整数

这里稍微逆向思考一下,既然不容易直接求结果,可不可以我猜一个值k

然后判断能不能通过某种切法使最小的那一块value>=k,也就是说,使16块的value都能大于等于k,

如果可以的话,我们就可以对[0, sum]这个区间进行二分查找。

二分的复杂度是log(sum).

接下来是重点:对于一个值,怎么判断能不能横竖切三刀,使16块的value都大于等于k呢

二分答案,判断可行性时暴力枚举三列的情况,然后横着贪心地扫一遍,

每当四列都满足的时候就砍一刀,满足四次即可

#include 
#include 
using namespace std;

//记录坐标点(i,j)之前的所有元素(包括坐标点(i,j)在内)之和
vector> get_sum(vector> &nums,int m,int n){
    vector> sums(m+1,vector(n+1,0));
    for(int i = 1;i <= m;++i){
        for(int j = 1;j <= n;++j){
            sums[i][j] = sums[i-1][j] + sums[i][j-1] - sums[i-1][j-1] + nums[i-1][j-1];
        }
    }
    return sums;
}

//计算(x1,y1)到(x2,y2)之间的面积, x1,y1不包含
int get_state(vector> &sums,int x1,int y1,int x2,int y2){
    return sums[x2][y2]+sums[x1][y1]-sums[x1][y2]-sums[x2][y1];
}

//把矩阵(土地)a[n][m]分割成4块,并且满足条件:各个块的值都不小于k;
bool isValid(vector> &sums,int m, int n, int target){
    for(int i = 1;i <= m-3;i++){
        for(int j = i+1;j <= m-2;j++){
            for(int k = j+1;k <= m;k++){
                int y1 = 0,cnt = 0;
                for(int y2 = y1+1;y2 <= n;y2++){
                    int s1 = get_state(sums,0,y1,i,y2);
                    int s2 = get_state(sums,i,y1,j,y2);
                    int s3 = get_state(sums,j,y1,k,y2);
                    int s4 = get_state(sums,k,y1,m,y2);
                    // 四列都满足时砍一刀,即y1 = y2操作
                    if(s1 >= target && s2 >= target && s3 >= target && s4 >= target){
                        cnt++;
                        y1 = y2;
                    } 
                    if(cnt >= 4) return true;
                }
            }
        }
    }
    return false;
}


int main(){
    int m,n;
    string s;
    while(cin >> m >> n){
        vector> nums(m,vector(n));
        for(int i = 0;i < m;++i){
            // 注意这里的输入没有空格,所以取到一个string上再进行操作!!!
            cin >> s;
            for(int j = 0;j < n;++j){
                nums[i][j] = s[j]-'0';
            }
        }

        vector> sums = get_sum(nums,m,n);

        // 二分法
        int i = 0,j = sums[m][n],mid,ans = 0;
        while(i <= j){
            mid = (i+j)/2;
            if(isValid(sums,m,n,mid)){
                i = mid+1;
                ans = mid;
            }
            else j = mid-1;
        }
        cout << ans << endl;
    }
}

 

你可能感兴趣的:(LeetCode刷题记录)