2022.01.09 Acwing寒假每日一题 打乱字母

1、题目打乱字母

农夫约翰将按字典序排列的 N 头奶牛的名字列表贴在了牛棚的门上。

每个奶牛的名字都由一个长度介于 1 到 20 之间的由小写字母构成的唯一字符串表示。

麻烦制造者贝茜将列表中的奶牛名字重新排序打乱了列表。

此外,她还对每头奶牛的名字中的字母顺序进行了重新排列(也可能保持不变)。

给定修改过后的列表,请帮助约翰确定列表中的每个名字可能出现在原始列表中的最低和最高位置。

输入格式
第一行包含整数 N。

接下来 N 行,按照修改过后列表的顺序,给出了修改过后的奶牛的名字。

输出格式
共 N 行,第 i 行输出给定的第 i 个字符串在原始列表中可能的最低和最高位置。

数据范围
1≤N≤50000
输入样例:
4
essieb
a
xzy
elsie
输出样例:
2 3
1 1
4 4
2 3
样例解释
无论如何,字符串 “a” 必然出现在原始列表中第一个,类似的,字符串 “xzy” 必然出现在原始列表中的最后一个。

而字符串 “essieb” 和 “elsie” 都有可能位于原始列表的第 2 位或第 3 位,这取决于它们的原始字母顺序。

例如,”bessie” 和 “elsie” 分别位于 2,3 位,”sisbee” 和 “ilees” 分别位于 3,2 位。

2、分析

思路上还是比较简单,利用贪心+二分查找可以很好解决问题。
具体思路如下:
原来的表是按照字典序进行排列的而原来的名称可能是现在名称所有字母的排列组合。
按照贪心的想法,当当前的名字的所有字母按照字母顺序从小到大排列并且其他所有名字是从大到小排列的时候,能取到最低位置,反之取到最高位置。

3、代码

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<string,string> pss;
pss all[50010];
int main()
{
    int n;
    cin >> n;
    //normal存储正常的字符串,ltoh存储按从低到高字母序排列的字符串,htol反之。all中存储二者
    vector<string> normal,htol,ltoh;
    for(int i = 0; i < n; i ++)
    {
        string tmp;
        cin >> tmp;
        normal.push_back(tmp);
        auto lh = tmp;
        sort(lh.begin(),lh.end());
        ltoh.push_back(lh);
        auto hl = lh;
        reverse(hl.begin(),hl.end());
        htol.push_back(hl);
        all[i] = {lh,hl};
    }
    // sort为后面二分查找位置做准备
    sort(htol.begin(),htol.end());
    sort(ltoh.begin(),ltoh.end());
    for(int i = 0; i < n; i ++)
    {
    	//first存的是从小到大的顺序的string
        auto tmp = all[i].first;
        // 我们找它在从大到小排列的字符串中排第几
        int l = 0, r = htol.size();
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(htol[mid] >= tmp) r = mid;
            else l = mid + 1;
        }
        cout << l + 1 << " ";
        
        //second存的是从大到小排列的string
        tmp = all[i].second;
        l = 0, r = ltoh.size();
        // 我们找它在从小到大排列的字符串中排第几。所有从小到大排列的字符串中包含他自己的从小到大的字符串,应该把这个字符串排除
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(ltoh[mid] >= tmp) r = mid;
            else l = mid + 1;
        }
        if(all[i].first == all[i].second) cout << l + 1 << endl;
        else cout << l << endl;
    }
}

4、总结

  1. 一开始看错题,弄了好久。一开始竟以为只按照首字母排序,,,
  2. 关于二分查找,如何表示找不到的情况。
    第一个方法
    在 n个顺序数组中找大于等于目标val的第一个值的时候,让 l = 0,r = n,其中n最后一个元素的后一个位置。这样当所找的值不存在时,返回的就是n,这也这题我的做法
    第二个方法
    在 n个顺序数组中找大于等于目标val的第一个值,返回的时候,如果l == n - 1。判断一下val与l对应的值的关系,根据二者关系做出判断然后返回。
    第三个方法
    可以使用upper_bound或者lower_bound实现
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值