3/2每日算法

<1>.删除公共字符

考点:哈希

描述

输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”

输入描述:

每个测试输入包含2个字符串

输出描述:

输出删除后的字符串

我的思路和代码:

#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
    string s1;
    string s2;
    getline(cin,s1);
    getline(cin,s2);
    //需要使用getline
    vector<int> hash(256,0);// 定义哈希数组因为说了是字符所以最多256个
    for(auto &s:s2)
    {
        hash[s]++;
    }//把s2放进哈希数组中
    string s3;
    for(int i=0;i<s1.size();i++)
    {
        if(hash[s1[i]]==0)
        s3.push_back(s1[i]);
    }//我采用的是多开一个空间,如果不在哈希数组中那就开始尾插
    cout<<s3;
    return 0;
}

比较好的思路

本题如果使用传统的暴力查找方式,如判断第一个串的字符是否在第二个串中,在再挪动字符删除这个字符 的方式,效率为O(N^2),效率太低,很难让人满意。 1. 将第二个字符串的字符都映射到一个hashtable数组中,用来判断一个字符在这个字符串。 2. 判断一个字符在第二个字符串,不要使用删除,这样效率太低,因为每次删除都伴随数据挪动。这里可 以考虑使用将不在字符添加到一个新字符串,最后返回新新字符串。

#include<iostream>
#include<string>
using namespace std;
int main()
{
// 注意这里不能使用cin接收,因为cin遇到空格就结束了。
// oj中IO输入字符串最好使用getline。
string str1,str2;
//cin>>str1;
//cin>>str2;
getline(cin, str1);
getline(cin, str2);
// 使用哈希映射思想先str2统计字符出现的次数
int hashtable[256] = {0};
for(size_t i = 0; i < str2.size(); ++i)
{
hashtable[str2[i]]++;
}
// 遍历str1,str1[i]映射hashtable对应位置为0,则表示这个字符在
// str2中没有出现过,则将他+=到ret。注意这里最好不要str1.erases(i)
// 因为边遍历,边erase,容易出错。
string ret;
for(size_t i = 0; i < str1.size(); ++i)
{
if(hashtable[str1[i]] == 0)
ret += str1[i];
}
cout<<ret<<endl;
return 0;
}

<2>.组队竞赛

链接:https://www.nowcoder.com/questionTerminal/6736cc3ffd1444a4a0057dee89be789b?orderByHotValue=1&page=1&onlyReference=false

来源:牛客网

牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。

例如:

一个队伍三个队员的水平值分别是3,3,3.那么队伍的水平值是3

一个队伍三个队员的水平值分别是3,2,3.那么队伍的水平值是3

一个队伍三个队员的水平值分别是1,5,2.那么队伍的水平值是2

为了让比赛更有看点,牛牛想安排队伍使所有队伍的水平值总和最大。

如样例所示:

如果牛牛把6个队员划分到两个队伍

如果方案为:

team1:{1,2,5}, team2:{5,5,8}, 这时候水平值总和为7.

而如果方案为:

team1:{2,5,8}, team2:{1,5,5}, 这时候水平值总和为10.

没有比总和为10更大的方案,所以输出10.

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
    int n=0;
    cin>>n;
    vector<long long> a(3*n);
    long long sum=0;
    for(int i=0;i<3*n;i++)
    {
        cin>>a[i];
    }
    sort(a.begin(),a.end());
    for(int i=0;i<n;i++)
    {
        sum+=a[3*n-(i+1)*2];
    }
    cout<<sum;
    return 0;  
}
//这道题目要得到最大水平就是数组的一个一个倒二的水平之和
//要用long long不然会通过不了全部的测试用例

来看看比较好的思路

队伍的水平值等于该队伍队员中第二高水平值,为了所有队伍的水平值总和最大的解法,也就是说每个队伍 的第二个值是尽可能大的值。所以实际值把最大值放到最右边,最小是放到最左边。 【解题思路】: 本题的主要思路是贪心算法,贪心算法其实很简单,就是每次选值时都选当前能看到的局部最优解,所 以这里的贪心就是保证每组的第二个值取到能选择的最大值就可以,我们每次尽量取最大,但是最大的 数不可能是中位数,所以退而求其次,取 每组中第二大的 例如 现在排序后 有 1 2 5 5 8 9 ,那么分组为1 8 9 和 2 5 5 关系arr[arr.length-2*(i+1)]

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
    // IO型OJ可能会有多组测试用例,所以这里要持续接收输入多组测试用例。
    int n;
    while (cin >> n)
    {
    long long sum = 0;
    vector<int> a;
    a.resize(3*n);
    for (int i = 0; i < (3 * n); i++)
    {
    cin >> a[i];
    }
    
    std::sort(a.begin(), a.end());
    for (int i = 0; i < n; i++) {
    sum+=a[a.size()-(2*(i+1))];
    }
    cout << sum << endl;
    }
}

<3>.排序子序列

链接:https://www.nowcoder.com/questionTerminal/2d3f6ddd82da445d804c95db22dcc471?orderByHotValue=1&page=1&onlyReference=false

来源:牛客网

牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列.

如样例所示,牛牛可以把数组A划分为[1,2,3]和[2,2,1]两个排序子序列,至少需要划分为2个排序子序列,所以输出2

非递减就是>=,非递增就是<=

【解题思路】: 解题思路: 1. 本题依次比较整个数组 2. a[i+1]>a[i] ,则进入非递减序列判断,直到遍历到下一个值不大于等于为止count++,然后进行下一位 置的判断 3. a[i+1]<a[i],则进入非递增序列判断,直到遍历到下一个值不小于等于为止count++,然后进行下一位 置的判断 4. a[i+1] == a[i]不进行操作,++i进行下一位置遍历,因为相等既可以属于非递增序列,也可以属于非递减 序列。

#include<iostream>
using namespace std;
#include<vector>
int main()
{
    int n=0;
    cin>>n;
    vector<int> a(n+1);
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    a[n]=0;//多开一个空间,存储0,因为要判断最后一个元素,因为如果最后只有一个单独元素的情况,也需要加入count,所以需要辅助空间
    int count=0;
    int i=0;
    while(i<n)
    {
        if(i<n&&a[i]>a[i+1])
        {
            while(i<n&&a[i]>=a[i+1])
            {
                i++;
            }
           count++;
           i++;
        }
        if(i<n&&a[i]<a[i+1])
        {
            while(i<n&&a[i]<=a[i+1])
            {
                i++;
            }
            count++;
            i++;
        }
        if(i<n&&a[i]==a[i+1])
            i++;
    }
    cout<<count;
    return 0;
}

本题注意点:本题开始比较a[i+1]与a[i]进行比较,为了避免越界,数组定义为n+1个,同时给a[n] = 0; a[n] = 0带来的影响,我们分为三种情况讨论: 1. 若到a[n-1] 的最后一组是非递减序列,当i==n-1,a[i] >a[i+1],因为前面的数都是大于0的,这个输入 条件已经说明了(去看看题目输入条件描述),里面的循环结束,i++,count++,i==n,外面的循环结 束。 2. 若到a[n-1] 的最后一组是非递增序列,当i==n-1,a[i] >a[i+1],因为前面的数都是大于0的,这个输入 条件已经说明了(去看看题目输入条件描述),循环再走一次,i++, i== n,里面的循环结束,i++, count++,i==n+1,外面的循环结束。 3. 第三种情况 1 2 1 2 1最后一个数是单独的情况,后面补个0,序列变成1 2 1 2 1 0,当走完全面的序列 i==n-1时,a[i] > a[i+1],进入判断出一个非递增序列,count++,i++,循环结束。 4. 也就是说数组最后一个位置多增加一个0,不会影响第1、2情况的判断,主要是帮助第3情况的正确判 断。

(这道题目对我来说烧脑了,呜呜)

<4>倒置字符串

描述

将一句话的单词进行倒置,标点不倒置。比如 I like beijing. 经过函数后变为:beijing. like I

输入描述:

每个测试输入包含1个测试用例: I like beijing. 输入用例长度不超过100

输出描述:

依次输出倒置之后的字符串,以空格分割

#include <iostream>
using namespace std;
#include<string>
#include<algorithm>
int main()
{
    string s1;
    getline(cin,s1);
    int start=0;
    for(int i=0;i<s1.size();i++)
    {
        if(s1[i]==' '||i==s1.size()-1)//到末尾一定要反转
        {
            if(i==s1.size()-1)//为了解决最后一个单词的反转,最后一个单词不是空格隔开的
            i++;
            reverse(s1.begin()+start,s1.begin()+i);
            start=i+1;
        }      
   }
   reverse(s1.begin(),s1.end());//先每个单词反转,最后整体反转
   cout<<s1;
   return 0;
}

发现一个取巧的方法

【解题思路2】: 第二思路是一个比较讨巧的思路,直接利用cin>>s接收输入,遇到空格就结束了,自然就分割开了每个单 词,其次将每次接收到的单词拼接到之前串的前面就逆置过来了

#include <iostream>
#include <string>
using namespace std;
// cin读取string时自动会被空格分隔开,用另一个字符串存储进行逆序输出
int main()
{
string s1, s2;
cin >> s2;
while (cin >> s1)
s2 = s1 + " " + s2;
cout << s2 << endl;
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值