农夫约翰将按字典序排列的 打乱字母(寒假每日一题 7)_贪心算法 头奶牛的名字列表贴在了牛棚的门上。

每个奶牛的名字都由一个长度介于 打乱字母(寒假每日一题 7)_算法_02打乱字母(寒假每日一题 7)_二分_03 之间的由小写字母构成的唯一字符串表示。

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

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

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

输入格式
第一行包含整数 打乱字母(寒假每日一题 7)_贪心算法

接下来 打乱字母(寒假每日一题 7)_贪心算法 行,按照修改过后列表的顺序,给出了修改过后的奶牛的名字。

输出格式
打乱字母(寒假每日一题 7)_贪心算法 行,第 打乱字母(寒假每日一题 7)_二分_07 行输出给定的第 打乱字母(寒假每日一题 7)_二分_07 个字符串在原始列表中可能的最低和最高位置。

数据范围
打乱字母(寒假每日一题 7)_C++_09

输入样例:

4
essieb
a
xzy
elsie

输出样例:

2 3
1 1
4 4
2 3

样例解释
无论如何,字符串 “a” 必然出现在原始列表中第一个,类似的,字符串 “xzy” 必然出现在原始列表中的最后一个。

而字符串 “essieb” 和 “elsie” 都有可能位于原始列表的第 打乱字母(寒假每日一题 7)_贪心算法_10 位或第 打乱字母(寒假每日一题 7)_字符串_11 位,这取决于它们的原始字母顺序。

例如,”bessie” 和 “elsie” 分别位于 打乱字母(寒假每日一题 7)_算法_12 位,”sisbee” 和 “ilees” 分别位于 打乱字母(寒假每日一题 7)_算法_13 位。


贪心 + 二分

  1. 对原字符串分别按字典降序和升序排列,并分别存储到b[]和c[]数组中
  2. 对b[]和c[]数组分别按字典序升序排列
  3. 如果要找到一个字符串的最前面的位置,需要这个字符串内部是升序(其它字符串按降序排列即b[]数组),然后在b[]数组中二分找到第一个>=此字符串的位置,将此位置输出即可
  4. 如果要找到一个字符串的最后面的位置,需要这个字符串内部是降序(其它字符串按升序排列即c[]数组),然后在b[]数组中二分找到最后一个<=此字符串的位置,将此位置输出即可
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 50010;

int n;
string a[N], b[N], c[N];

int main(){

cin >> n;
for(int i = 1; i <= n; i++){

cin >> a[i];
b[i] = c[i] = a[i];
sort(b[i].begin(), b[i].end(), greater<char>());
sort(c[i].begin(), c[i].end());
}

sort(b + 1, b + n + 1);
sort(c + 1, c + n + 1);

for(int i = 1; i <= n; i++){

sort(a[i].begin(), a[i].end());
int l = 1, r = n;
while(l < r){

int mid = l + r >> 1;
if(b[mid] >= a[i]) r = mid;
else l = mid + 1;
}
cout << r << ' ';

reverse(a[i].begin(), a[i].end());
l = 1, r = n;
while(l < r){

int mid = l + r + 1 >> 1;
if(c[mid] <= a[i]) l = mid;
else r = mid - 1;
}

cout << r << endl;
}

return 0;
}