()的多重嵌套.可能出现多个嵌套,一般方法都是递归或者用堆栈,这里用了一个“pass”标记,减轻了工作量。
因为带括号的化学式出现的形式是( ( ( )num1 )num2 )num3…,按下标顺序读入的字符串遇到的第一个数字num1,然后就会处理num1所在的化学式()num1.然后再往后处理num2所在的化学式()num2,依次处理.
所以可以在遇到数字之后,根据上一个位置是不是),找到最近的(.
#include
#include
#include
#include
#include
using namespace std;
map<string, int> mp[2];
typedef pair<string, int> Elem; // 定义元素,string 存 id , num 存 个数
// 造数: 注意下标要传引用或者指针,保证下标最终指向非数字
int toNumber(string str, int &pos) {
int flag = 0;
while (isdigit(str[pos]))
flag = flag * 10 + str[pos++] - '0';
return flag;
}
void solve(string str, int tag) {
int coe = 1, pos = 0; // coe: 系数, pos: 字符串下标
vector<Elem> arr; // 存元素的数组
if (isdigit(str[pos])) coe = toNumber(str, pos); //算出全局系数,也就是最前面的数字
while (pos < str.size()) {
if (isdigit(str[pos])) { // 遇到数字,就开始计算,除了全局系数,其他系数都存在于元素末尾
int num = toNumber(str, pos);
int i = arr.size() - 1;
if (arr[i].first == ")") { // 数字前一位是')',那么就要一个 '(' , ')' 组合内的所有元素乘倍数
arr[i].first = "pass"; // 并修改标记,表示已经用过,避免嵌套的括号二次利用
while (arr[--i].first != "(") arr[i].second *= num; // 找到对应的'('并修改标记
arr[i].first = "pass";
}
else arr[i].second *= num; // 若数字前不是括号,则为元素本身,那么直接乘倍就好了
}
else if (str[pos] == '(') { //直接入数组
arr.push_back(Elem("(", 0));
pos++;
}
else if (str[pos] == ')') {
arr.push_back(Elem(")", 0));
if (!isdigit(str[++pos])) str.insert(pos, "1"); // 如果')'后面没有数字,那么我们就给他一个‘1’,保证所有都是')'数字结尾
}
else if (isupper(str[pos])) { // 遇到元素
string name = "";
name += str[pos];
pos++;
if (islower(str[pos])) { //若第二位为小写
name += str[pos];
pos++;
}
arr.push_back(Elem(name, 1)); //默认初始值都是 1
}
}
for (int i = 0; i != arr.size(); i++) {
if (arr[i].first == "pass" ) continue; // 遇到非元素直接pass
mp[tag][arr[i].first] += arr[i].second * coe;
}
}
bool Judge(string str) {
mp[0].clear();
mp[1].clear();
int pos = str.find('=');
string tmp;
string left = str.substr(0, pos);
string right = str.substr(pos + 1);
stringstream left_ss(left), right_ss(right);
while (getline(left_ss, tmp, '+')) solve(tmp, 0);
while (getline(right_ss, tmp, '+')) solve(tmp, 1);
if (mp[0].size() != mp[1].size()) return false;
for (map<string, int>::iterator it = mp[0].begin(); it != mp[0].end(); it++)
if (mp[1][it->first] != it->second) return false;
return true;
}
int main() {
int n;
string str;
cin >> n;
while (n--) {
cin >> str;
if (Judge(str)) cout << "Y" << endl;
else cout << "N" << endl;
}
return 0;
}
参考:代码一
思路:
利用unordered_map
先按=将整个方程式分成两部分。左部分所有原子默认基本系数为1,右部分所有原子默认基本系数为-1。每部分最终的原子个数要乘上这个基本系数,这样处理完整个方程式中所有原子,如果配平成功所有原子对应个数应该均为0;否则有原子个数不为0 。
对于按=将分成的两部分,再按+分成多个化学式。针对每个化学式统计每种原子出现的个数。
如何处理()?这里采取递归处理的方法,针对遇到的每个(,找出其对应的位置,递归处理该()的化学式,注意此时该()内的系数要乘上该()后紧邻的数字.
找出(对应的)的方法是,设立一个num,初始化为1,遍历(之后的所有字符,遇到一个(就让num加1,遇到一个)就让num减1,那么使num==0的)字符即为(对应的).
这种方法与代码一中的方法也不同,而且比栈更好用.
else if (formula[i] == '(') { //遇到(
for (int num = 1; num != 0; ++j) { //找到对应的)位置存储在j中
if (formula[j] == '(')
++num;
else if (formula[j] == ')')
--num;
}
int k = j;
f(i + 1, k - 1, e * computeDigit(j, last)); //递归处理
}
参考:代码二
总结:大型模拟题,细心.