codeforces 题目 awoo‘s Favorite Problem

目录

题目:

题目描述:

思路:

AC代码:


题目:

题目描述:

给你长度为 n 的两个只包含 ' a ' , ' b ' , ' c ' 的字符串 s 和 t 

你能进行的操作:对串中相邻的两个字符,你可以将 ' ab ' 转化为 ' ba ' , 或者将 ' bc ' 转化为 ' cb

问:能不能经过任意次操作,将 s 字符串 转换为 t 字符串

思路:

如果我们对 s 串从前往后与t串进行比对,我们会发现,只有碰到不一样的才需要从后面找,但我们会发现,根据题目对操作的要求,后面的 ' a ' 不能跨越前面的 ' b ' 或 ' c ' ,后面的 ' b ' 不能跨越前面的 ' c ' ,后面的 ' c ' 不能跨越前面的 ' a ' 

具体关系如下图所示:

(解释: a → b 表示 a 可以从右边向左跨越 b ,红色箭头表示不能从右往左跨越。)

所以我们可以分类讨论,在遍历的过程中,如果一样就继续,如果不一样:

如果 t 串在这个位置需要的是 ' a ' :

那就别想了,后面的 a 无论如何也往前挪不动,因为 ' a ' 无法跨越 ' b ' 或者 ' c ' 

②如果 t 串在这个位置需要的是 ' b ' :

那么我们肯定是找 s 串这个位置后边出现的第一个 ' b ' 来挪到这个位置。但是如果后面没有 ' b ' 了,或者中间隔了 ' c ' 那就一定错误,因为 ' b ' 不可能从右往左跨越 ' c ' 。但如果找到了就进行交换

③如果 t 串在这个位置需要的是 ' c ' :

那么我们肯定是找 s 串这个位置后边出现的第一个 ' c ' 来挪到这个位置。但是如果后面没有 ' c ' 了,或者中间隔了 ' a ' 那就一定错误,因为 ' c ' 不可能从右往左跨越 ' a ' 。但如果找到了就进行交换

所以我们现在需要的是什么?一种能快速找到后面第一次出现的字符的位置的数据结构,所以我想到了 set 容器( 对放入的元素自动排列 )

我们可以开三个set分别来存放 ' a ' , ' b ' , ' c ' 的出现位置,这样用 set 中的 begin 位置存放的数据就是这个字符在后面第一次出现的位置。

思路有了,具体操作请看AC代码:

AC代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

char s[N];
char t[N];

int main()
{
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int q; cin >> q;

	while (q--)
	{
		int n;
		cin >> n;

		set<int>a;
		set<int>b;
		set<int>c;

		cin >> s + 1;
		cin >> t + 1;

		int flag = 1;//代表是否能成功,1成功,0失败

		for (int i = 1; i <= n; i++)
		{
			if (s[i] == 'a')a.insert(i);
			else if (s[i] == 'b')b.insert(i);
			else c.insert(i);
		}

		for (int i = 1; i <= n; i++)
		{
			//遍历到每一位,先删除对应set中的第一位
			if (s[i] == 'a')a.erase(i);
			else if (s[i] == 'b')b.erase(i);
			else c.erase(i);

			//如果一样就下一位
			if (s[i] == t[i])continue;
			//如果不一样就分类讨论
			if (t[i] == 'a')
			{
				flag = 0;
				break;
			}
			else if (t[i] == 'b')
			{
				if (s[i] == 'c' || b.size() == 0 ||
					(c.size() != 0 && *c.begin() < *b.begin()))
				{
					flag = 0;
					break;
				}
				//如果找到了符合要求的字符,进行交换
				swap(s[i], s[*b.begin()]);
				//set中也要更新
				a.insert(*b.begin());
				b.erase(b.begin());
			}
			else if (t[i] == 'c')
			{
				if (s[i] == 'a' || c.size() == 0 ||
					(a.size() != 0 && *a.begin() < *c.begin()))
				{
					flag = 0;
					break;
				}
				swap(s[i], s[*c.begin()]);
				b.insert(*c.begin());
				c.erase(c.begin());
			}
		}

		if (flag)
			cout << "YES" << '\n';
		else
			cout << "NO" << '\n';
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值