化學方程式
這是一個化學方程式檢測的算法題,只是檢測物料守恆,題目不是特別難,但是字符串處理時還是挺繁瑣的,假期一直宅在家裏,好久沒更新博客了。算是記錄一下解題的思路把。
下面是完整代碼
#include <iostream>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;
struct element {
string s;
int amount;
element() :s(""), amount(0) {};
element(string st) :s(st), amount(0) {};
element(string st, int a) :s(st), amount(a) {};
bool operator < (const element& e) {
return s < e.s;
}
bool operator == (const element& e) {
return s == e.s;
}
};
class elements {
public:
vector<element> es;
elements() {};
elements(element e) {
es.push_back(e);
}
void add_element(string ele_name, int amount) {
element t_e = element(ele_name, amount);
auto i = find(es.begin(), es.end(), t_e);
if (i == es.end()) {
es.push_back(t_e);
}
else {
i->amount += amount;
}
}
void show() {
for (auto t : es) {
cout << t.s << '\t' << t.amount << endl;
}
cout << endl << endl;
}
void up_elements(elements& e_t, int mul_num) {
for (auto i : e_t.es) {
auto j = find(es.begin(), es.end(), i);
if (j == es.end()) {
i.amount *= mul_num;
es.push_back(i);
}
else {
j->amount += i.amount * mul_num;
}
}
}
bool operator == (const elements& es_t) {
if (es_t.es.size() != es.size()) {
return false;
}
for (int i = 0; i < es.size(); i++) {
if (es[i].s != es_t.es[i].s || es[i].amount != es_t.es[i].amount) {
return false;
}
}
return true;
}
bool is_equal(elements& es_t) {
sort(es.begin(), es.end());
sort(es_t.es.begin(), es_t.es.end());
if (*this == es_t) {
return true;
}
return false;
}
};
elements r_left, r_right;
vector<string> l_s, r_s;
char result[100];
stringstream ss;
void split_s(string s) {
ss.clear();
ss.str(s);
string tmp[2];
int i_t = 0;
string tmp_s;
while (getline(ss, tmp[i_t++], '='));
ss.clear();
ss.str(tmp[0]);
while (getline(ss, tmp_s, '+')) {
l_s.push_back(tmp_s);
}
ss.clear();
ss.str(tmp[1]);
while (getline(ss, tmp_s, '+')) {
r_s.push_back(tmp_s);
}
}
elements cal(string s) {
elements r_tmp;
int prefix_num;
string prefix_str;
string e_s_t;
int j_tmp = 0;
while (s[j_tmp] >= '0' && s[j_tmp] <= '9') {
prefix_str += s[j_tmp];
j_tmp++;
}
prefix_num = prefix_str.size() ? stoi(prefix_str) : 1;
while (1) {
if (j_tmp < s.size()) {
if (s[j_tmp] >= 'A' && s[j_tmp] <= 'Z') {
if (e_s_t.size()) {
r_tmp.add_element(e_s_t, prefix_num);
e_s_t.clear();
}
e_s_t += s[j_tmp];
j_tmp++;
}
else if (s[j_tmp] >= 'a' && s[j_tmp] <= 'z') {
e_s_t += s[j_tmp];
j_tmp++;
}
else if (s[j_tmp] >= '0' && s[j_tmp] <= '9') {
string inner_str;
int inner_num;
while (j_tmp < s.size() && s[j_tmp] >= '0' && s[j_tmp] <= '9') {
inner_str += s[j_tmp];
j_tmp++;
}
inner_num = inner_str.size() ? stoi(inner_str) : 1;
r_tmp.add_element(e_s_t, inner_num * prefix_num);
e_s_t.clear();
}
else if (s[j_tmp] == '(') {
if (e_s_t.size()) {
r_tmp.add_element(e_s_t, prefix_num);
e_s_t.clear();
}
int pos1 = j_tmp + 1;
int flag = 0;
while (1) {
if (s[j_tmp] == '(') {
flag++;
}
if (s[j_tmp] == ')') {
flag--;
}
if (flag == 0) {
j_tmp++;
break;
}
j_tmp++;
};
int pos2 = j_tmp - 1;
int size_tmp = pos2 - pos1;
string deep_string = s.substr(pos1, size_tmp);
elements r_tmp_e = cal(deep_string);
string inner_str;
int inner_num;
if (j_tmp < s.size() && s[j_tmp] >= '0' && s[j_tmp] <= '9') {
while (j_tmp < s.size() && s[j_tmp] >= '0' && s[j_tmp] <= '9') {
inner_str += s[j_tmp];
j_tmp++;
}
}
inner_num = inner_str.size() ? stoi(inner_str) : 1;
r_tmp.up_elements(r_tmp_e, inner_num* prefix_num);
}
}
else {
if (e_s_t.size()) {
r_tmp.add_element(e_s_t, prefix_num);
e_s_t.clear();
}
break;
}
}
return r_tmp;
}
void calc() {
elements e_tmp;
for (auto& i : l_s) {
e_tmp = cal(i);
r_left.up_elements(e_tmp, 1);
}
for (auto& i : r_s) {
e_tmp = cal(i);
r_right.up_elements(e_tmp, 1);
}
l_s.clear();
r_s.clear();
}
void sol() {
int n;
cin >> n;
string tmp_s;
for (int i = 0; i < n; i++) {
cin >> tmp_s;
split_s(tmp_s);
calc();
if (r_left.is_equal(r_right)) {
result[i] = 'Y';
}
else {
result[i] = 'N';
}
//r_left.show();
//r_right.show();
r_left.es.clear();
r_right.es.clear();
}
int pos = 0;
while (result[pos] == 'N' || result[pos] == 'Y') {
cout << result[pos]<<endl;
pos++;
}
}
int main() {
sol();
return 0;
}
這裏的核心算法其實就是cal函數,這個函數主要是拆分字符串爲基本元素符號,並統計個數,比如說將"2H2O拆分成兩個struct,e1=element(‘H’,4),e2=element(‘O’,2)",下面分析一下這個函數.
struct element {
string s;
int amount;
element() :s(""), amount(0) {};
element(string st) :s(st), amount(0) {};
element(string st, int a) :s(st), amount(a) {};
bool operator < (const element& e) {
return s < e.s;
}
bool operator == (const element& e) {
return s == e.s;
}
};
class elements {
public:
vector<element> es;
};
elements cal(string s) {
elements r_tmp;
int prefix_num;
string prefix_str;
string e_s_t;
int j_tmp = 0;
while (s[j_tmp] >= '0' && s[j_tmp] <= '9') {
prefix_str += s[j_tmp];
j_tmp++;
}
prefix_num = prefix_str.size() ? stoi(prefix_str) : 1;
while (1) {
if (j_tmp < s.size()) {
if (s[j_tmp] >= 'A' && s[j_tmp] <= 'Z') {
if (e_s_t.size()) {
r_tmp.add_element(e_s_t, prefix_num);
e_s_t.clear();
}
e_s_t += s[j_tmp];
j_tmp++;
}
else if (s[j_tmp] >= 'a' && s[j_tmp] <= 'z') {
e_s_t += s[j_tmp];
j_tmp++;
}
else if (s[j_tmp] >= '0' && s[j_tmp] <= '9') {
string inner_str;
int inner_num;
while (j_tmp < s.size() && s[j_tmp] >= '0' && s[j_tmp] <= '9') {
inner_str += s[j_tmp];
j_tmp++;
}
inner_num = inner_str.size() ? stoi(inner_str) : 1;
r_tmp.add_element(e_s_t, inner_num * prefix_num);
e_s_t.clear();
}
else if (s[j_tmp] == '(') {
if (e_s_t.size()) {
r_tmp.add_element(e_s_t, prefix_num);
e_s_t.clear();
}
int pos1 = j_tmp + 1;
int flag = 0;
while (1) {
if (s[j_tmp] == '(') {
flag++;
}
if (s[j_tmp] == ')') {
flag--;
}
if (flag == 0) {
j_tmp++;
break;
}
j_tmp++;
};
int pos2 = j_tmp - 1;
int size_tmp = pos2 - pos1;
string deep_string = s.substr(pos1, size_tmp);
elements r_tmp_e = cal(deep_string);
string inner_str;
int inner_num;
if (j_tmp < s.size() && s[j_tmp] >= '0' && s[j_tmp] <= '9') {
while (j_tmp < s.size() && s[j_tmp] >= '0' && s[j_tmp] <= '9') {
inner_str += s[j_tmp];
j_tmp++;
}
}
inner_num = inner_str.size() ? stoi(inner_str) : 1;
r_tmp.up_elements(r_tmp_e, inner_num* prefix_num);
}
}
else {
if (e_s_t.size()) {
r_tmp.add_element(e_s_t, prefix_num);
e_s_t.clear();
}
break;
}
}
return r_tmp;
}
上面的代碼沒怎麼寫註釋,習慣了,儘管知道不太好,但是一直沒改。
下面說一下字符串分解函數
- 計算前綴數字,也就是化學物質的摩爾數
- 遍歷元素,會遇到5種情況
- 1)大寫字母,這時如果前面不是數字,則需要存儲前面的element,數量爲前綴長度,然後遍歷下一個
- 2)小寫字母,這時遍歷下一個字符
- 3)數字,這裏有一個注意點,需要注意邊界,這是需要進行添加element操作
- 4)括號,這是最麻煩的一部,這裏有一個比較坑的地方是需要注意括號嵌套括號的情況,所以使用了下面的一段代碼,接着便是遞歸。
int flag = 0;
while (1) {
if (s[j_tmp] == '(') {
flag++;
}
if (s[j_tmp] == ')') {
flag--;
}
if (flag == 0) {
j_tmp++;
break;
}
j_tmp++;
};
- 5)字符遍歷越界,這裏也是需要判斷處理的。
這道題還是花了很長時間才做出來的,如果再考場上的話,拿不到滿分,能解決還是很happy的。