#include <iostream>
#include <unordered_map>
using namespace std;
unordered_map<string, int> record; //用于记录元素对应的数量
int num = 0; //用于最后验证左右原子数相同
bool recursion(string cur, bool add, int time); //用于更新record中元素对应的数量,cur为当前字符串,add为true时增加元素,反之减少元素,time是倍数
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
string cur;
cin >> cur;
int equal = 0; //用于记录等号的位置
while (cur[equal] != '=') equal++; //寻找等号的位置
record.clear(); //由于需要判断不止一条方程式,record是全局变量,每次循环需要先清空
num = 0; //等式左边总原子数初始化为0
recursion(cur.substr(0, equal), true, 1); // 将等号左边的整个式子送入递归,增加record中元素对应的数量,基础倍数为1
if (!recursion(cur.substr(equal + 1), false, 1) || num != 0) { //将等号右边的整个式子送入递归,减少record中元素对应的数量,基础倍数为1,若返回false,则输出N,反之输出Y;或者等式左右原子数不相等也输出N
cout << "N" << endl;
}
else {
cout << "Y" << endl;
}
}
return 0;
}
bool recursion(string cur, bool add, int time) {
if (cur.empty()) return true; //若式子为空,返回true
int plus = 0; //用于记录第一个加号的位置
while (plus < cur.size() && cur[plus] != '+') plus++; //第一个加号的位置,若无加号则为式子长度
if (plus < cur.size()) { //如果含有加号,将式子分为加号左边和加号右边两部分送入递归,下面的所有add值等于传来的add值,表示加元素或减元素(原谅我取变量名偷懒了)
return recursion(cur.substr(0, plus), add, time) && recursion(cur.substr(plus + 1), add, time);
}
else { //如果不含加号,这就是一个带有显式系数或系数为1的一个化学式
if (cur[0] >= '0' && cur[0] <= '9') { //带有显式系数时
int num_over = 1; //用于记录下一个需要判断是否是数字的字符位置
int ratio = cur[0] - '0'; //记录第一个数字,用作化学式的倍率
while (cur[num_over] >= '0' && cur[num_over] <= '9') { //直到下一个字符不是数字为止,更新化学式的倍率
ratio *= 10;
ratio += cur[num_over++] - '0';
}
return recursion(cur.substr(num_over), add, time * ratio); //现在知道了化学式的倍率,直接把后面的东西丢给递归去解决,注意递归倍率要乘上一层的倍率
}
else if (cur[0] == '(') { //化学式默认系数为1且第一个字符为左括号时
int bracket_num = 1; //用于记录左括号的数量
int right = 1; //用于记录第一个左括号对应的右括号的位置,以下简称对应右括号
while (bracket_num > 0) { //循环直到找到对应右括号,其实这里可以用stack栈操作,左括号入栈右括号出栈当栈为空时就是对应右括号的位置
if (cur[right] == '(') bracket_num++;
else if (cur[right] == ')') bracket_num--;
right++;
}
int temp = right; //用临时变量记录对应右括号位置
int ratio = 0; //接下来要看对应右括号右边有无数字,倍率先设置为0
while (right < cur.size() && cur[right] >= '0' && cur[right] <= '9') { //从左到右读出数字
ratio *= 10; //知道倍率为什么要先设置成0了吧~
ratio += cur[right] - '0';
right++;
}
if (ratio == 0) ratio = 1; //如果对应右括号右边没有数字,则倍率为1
//接下来将第一个最外层括号里的内容丢给递归,然后把后面的内容也丢给递归,这两部分内容都可能仍含有括号,但是没关系递归就是干这事的,还要注意第一个递归倍率要乘上一层的倍率
return(recursion(cur.substr(1, temp - 2), add, time * ratio)) && recursion(cur.substr(right), add, time);
}
else { //化学式默认系数为1且第一个字符不为左括号时
int lower = 1; //用于记录第一个元素后面的位置,用小写字母找是因为元素最多含有1个小写字母且在右边
while (lower < cur.size() && cur[lower] >= 'a' && cur[lower] <= 'z') lower++; //寻找第一个元素后面的位置
string temp = cur.substr(0, lower); //取出完整元素符号
int ratio = 0; //接下来寻找倍率的操作同“对应右括号右边倍率”
while (lower < cur.size() && cur[lower] >= '0' && cur[lower] <= '9') {
ratio *= 10;
ratio += cur[lower] - '0';
lower++;
}
if (ratio == 0) ratio = 1;
if (add) { //如果add为true,说明是等式左边的式子,则增加对应元素的数量,注意要用这一层倍率乘上上一层的倍率
record[temp] += time * ratio;
num += time * ratio; //原子数加
}
else { //如果add为false,说明是等式右边的式子,则减少对应元素的数量
if (!record.count(temp)) return false; // 如果等式左边的化学式不含有等式右边化学式的元素则说明没配平,返回false
record[temp] -= time * ratio;
num -= time * ratio; //原子数减
if (record[temp] < 0) return false; //如果减多了说明没配平,返回false
}
return recursion(cur.substr(lower), add, time); //这个元素后面可能仍含有化学式,送入递归,若为空字符串,对应函数开头直接返回true
}
}
}