贪心+二分
思路:
开三个数组,r存原来的字符串,u存字典序重组后的字符串,d存倒字典序存储的字符串(贪心)
如输入:cdab r: cdab u:abcd d:dcba
之后把u和d升序排序
遍历r,把当前的字符串:
①正字典序重组(使其变成字典序大小最小的情况),找到它在d数组中的左边界a;
②倒字典序重组(使其变成字典序大小最大的情况),找到它在d数组中的右边界b;
③输出a和b
由于d数组中的字符串都是字典序最大的情况,而①中字符串是其最小的情况,所以能找到所有情况的最左边界
u同理
注意!:在存r数组的时候,如果一开始存的是原来的字符串,那么在下面处理的时候就要重新多sort一次它,就会TLE,所以我们的r是没有排序的u数组
更新:把正字典序排序变成倒字典序排序的时候可以用reverse(s.begin(), s.end()),复杂度是O(n)的,比sort快
代码:
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int n;
vector<string> r, u, d;//u(up)存储字典序排列的字符串,d(down)存储倒字典序排列的字符串
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ )
{
string s;
cin >> s;
sort(s.begin(), s.end(), less()); //字符串正字典序排列
u.push_back(s);
r.push_back(s);//r数组存的是正字典序排列的字符串,方便之后处理(因为题目要求按照输入的顺序输出)
//sort(s.begin(), s.end(), greater()); //字符串倒字典序排列
reverse(s.begin(), s.end());
d.push_back(s);
}
sort(u.begin(), u.end(), less());//u中都是字典序排列字符串(升序,每个都是最小情况)
sort(d.begin(), d.end(), less());//d中都是倒字典序排列的字符串(升序,每个都是最大情况)
for(auto &s : r)
{
//最小的情况在最大的数组里面找(左边界),最大的情况在最小的里面找(右边界)
//此时s它是最小的(因为一开始r存的就是把他正字典序重排的结果),所以在倒字典序排列的字符串(字符串都是最大的情况)中找它的最小位置
int a = lower_bound(d.begin(), d.end(), s) - d.begin() + 1;//因为从1开始标号,所以+1
//sort(s.begin(), s.end(), greater());//将s倒字典序排列重组
reverse(s.begin(), s.end());
//此时s是最大的,在字典序排列的字符串(都是最小的情况)中找他的最大位置
int b = upper_bound(u.begin(), u.end(), s) - u.begin();//因为找的是第一个大于s的位置,所以-1再+1
cout << a << ' ' << b << endl;
}
return 0;
}
这就是小清华的实力嘛 太狠了
e哥关注了
hxd
byd
我2144ms
同理最高位置也是一样的,就是你在任何一个二分的数组都是包含所有牛的,那就包含自身呀,怎么保证二分出来的结果里没有自己?
对于字符串s,对于左边界,在d数组中他是倒字典序重组的情况,那么正字典序的s一定小于等于倒字典序的s。对于右边界,分两种情况:①s是回文串,二分找到的是大于它的那个位置,所以他应该在的位置是要-1的,②s不是回文串,那么二分找到的就是他应该在的位置,这里再-1,是因为前面包含了正字典序的s,把它去掉。
虽然upper_bound都减了1,我觉得它实际的意义是不一样的~
对每个s去找最低位置的时候,在d数组中,这时d数组是包含所有奶牛的啊,不应该把这个s删掉,再去找s的位置吗?如果排好序的d数组中存在牛s,他的位置假设为2,但是你对正序s去二分,可能得到位置是5,那前4个位置包含他自身了啊,这种情况不需要判断吗?
①正字典序重组(使其变成字典序大小最小的情况),找到它在d数组中的左边界a;
楼主为啥是d数组,不是u数组吗?贪心。为了尽可能让字符串的左边界靠左,那么就要让数组中的字符串尽可能大,这样小的字符串放进去就会在最左边。
e
233