首页 > 其他分享 >洛谷 P2412 查单词

洛谷 P2412 查单词

时间:2023-02-06 10:46:03浏览次数:56  
标签:P2412 洛谷 tolower int 数组 单词 str ori

这是一个非常有意思的题……

这一个题放在线段树里面显得非常清奇。很多题解并没有用线段树,或者是用的线段树方法很难。因此,这里为初学者献上一份较为简单容易看懂的代码。

题目大意:

一共有\(n\)个单词和\(m\)次查询,其中第\(1\)~\(n\)行输入单词。前\(n\)行输入单词,后\(m\)行输入两个数\(l\),\(r\),表示求出区间\([l,r]\)内字典序最大的单词。

注意:在排序中不在乎大小写进行字典序排序,但输出须输出原单词。例如:abc和DEF,虽然大写的字典序要比小写的要小,但是由于在排序过程中不在乎大小写,因此答案还是输出DEF。同时,题目限时0.2秒。

解题思路:

因为很多人看到题目中出现了限时0.2秒就放弃了线段树而采用更快的ST表,但是这道题加上线段树的标签是有原因的(那就是可以用线段树解)。

如果要用线段树,无非就是模板中的那几个模块:push_up,push_down,build,update,query。观察这道题,因为没有修改操作,只有查询操作,因此只保留push_up,build和query这三个模块。

需要的数组及变量

sum[]数组:用于创建线段树。

a[]数组:用于存储排序后的单词编号。

node{}结构体:存储单词及其输入的编号。

tolower_str[]数组:用于存储转换为小写后的单词及编号,同时用于排序。

ori_str[]数组:用于存储原单词和编号,用于输出答案。

backup[]数组:用于备份原数组。原数组后面可能需要进行数据调整,有一个备份始终比没有好。

nmxy变量:题目所给。

设计代码

push_up()函数

因为题目要求最大值,那么我们就维护一个树根保存的是最大值的线段树sum

因此,当前根节点的最大值就是这个节点左儿子和右儿子的最大值。

当然,我们的sum存储的不是单词,而是单词的编号。找到了单词的编号,就能对应输出字典序最大的单词。

void push_up(int root) {
        sum[root] = max(sum[root << 1], sum[root << 1 | 1]);
}

build()函数

build()函数就是建树,和模板一毛一样。

只是需要注意:a[]数组需要保存的是排过序后的单词的编号(可以看↓下面的主函数)。

void build(int l, int r, int root) {
        if (l == r) {
                sum[root] = a[l];
                return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, root << 1);
        build(mid + 1, r, root << 1 | 1);
        push_up(root);
}

query()函数

我们建的树的树根维护的是最大字典序单词的编号,因此输出的答案也应该是取最大值。

所以,这道题的query()函数和模板就没有区别,只是ans那个地方取得是最大值。

因为返回的是最大字典序单词的编号,故函数的返回值是int

int query(int ledge, int redge, int l, int r, int root) {
        if (ledge <= l && r <= redge)
                return sum[root];
        int mid = (l + r) >> 1;
        int ans = -inf;
        if (ledge <= mid)
                ans = max(ans, query(ledge, redge, l, mid, root << 1));
        if (redge > mid)
                ans = max(ans, query(ledge, redge, mid + 1, r, root << 1 | 1));
        return ans;
}

main()主函数

正常读入就不多说了。

存储单词

首先,我们需要读入这个单词,是用临时字符串str来存储。

接下来,因为题目说要输出原单词而不是全部变成小写后的单词,我们需要将这个单词和编号一起存入ori_str[]数组里(目的是为了输出原单词)。

之后,我们需要将这个单词转换成小写存在tolower_str[]数组里面(因为题目说过:字典序大小不和字母大小写有关)。同时,把编号一起存入tolower_str[]

for (int i = 1; i <= n; i++) {
        cin >> temp;
        ori_str[i].s = temp;
        ori_str[i].id = i;
        backup[i] = ori_str[i];
        transform(temp.begin(), temp.end(), temp.begin(), ::tolower);
        tolower_str[i].s = temp;
        tolower_str[i].id = i;
}

处理存储好的单词和建树

首先,已经转换好的单词就是用sort()函数从小到大排序。(如果不清楚为什么要从小到大排序可以手动模拟一下样例数据)

sort(tolower_str + 1, tolower_str + 1 + n);

然后,ori_str[]就是将排好序的单词进行存储。因为单词是要被从小到大处理一遍,也就是说单词的顺序可能会出现变化,这时候backup[]数组的作用就体现出来了。它存储的是原始单词和编号,因此直接从backup[]数组中以排好序后的顺序倒入到ori_str[]数组中。

for (int i = 1; i <= n; i++) 
        ori_str[i] = backup[tolower_str[i].id];

最后,因为我们是以从小到大的顺序进行了单词的处理,因此我们现在需要将它的编号也以同样的顺序进行处理。

因为我们是以单词的编号进行建树,并且在处理过程中将单词以从小到大的顺序进行了排序,因此我们的单词编号也需要按照排序后的顺序进行存储,这样才能建正确的数。

可以这么理解:a[]数组是我们的单词编号线段树,而ori_str[]数组是我们的单词线段树。

for (int i = 1; i <= n; i++) 
        a[tolower_str[i].id] = i;
build(1, n, 1);

最后就是输出啦。因为我们的ori_str[]数组就是我们的单词线段树,query()函数就是寻找在\([l,r]\)区间内字典序最大的单词数的下标,因此直接读出ori_str[query()]就是我们的答案。(这一步就是模板中的query(),只是答案是string必须要将下标返回给数组输出原单词)。

for (int i = 1; i <= m; i++) {
        cin >> x >> y;
        cout << ori_str[query(x, y, 1, n, 1)].s << endl;
}

完整代码

#include <bits/stdc++.h>
#define lson l, mid, root << 1
#define rson mid + 1, r, root << 1 | 1
#define lroot root << 1
#define rroot root << 1 | 1
#define inf 0x3f3f3f3f
using namespace std;
const int N = 5e4 + 10;
int n, m, a[N], x, y;
int sum[N << 2];
struct word {
        string s;
	int id;
	bool operator < (const word W) const {
		return s < W.s;
	}
} tolower_str[N], ori_str[N], backup[N];
string temp;
void push_up(int root) {
	sum[root] = max(sum[lroot], sum[rroot]);
}
void build(int l, int r, int root) {
	if (l == r) {
		sum[root] = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	push_up(root);
}
int query(int ledge, int redge, int l, int r, int root) {
	if (ledge <= l && r <= redge)
		return sum[root];
	int mid = (l + r) >> 1;
	int ans = -inf;
	if (ledge <= mid)
		ans = max(ans, query(ledge, redge, lson));
	if (redge > mid)
		ans = max(ans, query(ledge, redge, rson));
	return ans;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> temp;
		ori_str[i].s = temp;
		ori_str[i].id = i; 
		backup[i] = ori_str[i];
		transform(temp.begin(), temp.end(), temp.begin(), ::tolower);
		tolower_str[i].s = temp;
		tolower_str[i].id = i;
	}
	sort(tolower_str + 1, tolower_str + 1 + n);
	for (int i = 1; i <= n; i++) 
		ori_str[i] = backup[tolower_str[i].id];
	for (int i = 1; i <= n; i++) 
		a[tolower_str[i].id] = i;
	build(1, n, 1);
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		cout << ori_str[query(x, y, 1, n, 1)].s << endl;
	}
	return 0;
}

给个赞吧!

标签:P2412,洛谷,tolower,int,数组,单词,str,ori
From: https://www.cnblogs.com/GTGumiiL/p/17094365.html

相关文章