Solve a given equation and return the value of x
in the form of string "x=#value". The equation contains only '+', '-' operation, the variable x
and its coefficient.
If there is no solution for the equation, return "No solution".
If there are infinite solutions for the equation, return "Infinite solutions".
If there is exactly one solution for the equation, we ensure that the value of x
is an integer.
Example 1:
Input: "x+5-3+x=6+x-2" Output: "x=2"
Example 2:
Input: "x=x" Output: "Infinite solutions"
Example 3:
Input: "2x=x" Output: "x=0"
Example 4:
Input: "2x+3x-6x=x+2" Output: "x=-1"
Example 5:
Input: "x=x+2" Output: "No solution"
這道題給了我們一個用字符串表示的方程式,讓我們求出x的解,根據例子可知,還包括x有無窮多個解和x沒有解的情況。解一元一次方程沒什么難度,難點在於處理字符串,如何將x的系數合並起來,將常數合並起來,化簡成ax=b的形式來求解。博主最開始的思路是先找到等號,然后左右兩部分分開處理。由於要化成ax=b的格式,所以左半部分對於x的系數都是加,右半部分對於x的系數都是減。同理,左半部分對於常數是減,右半部分對於常數是加。
那么我們就開始處理字符串了,我們定義一個符號變量sign,初始化為1,數字變量num,初始化為-1,后面會提到為啥不能初始化為0。我們遍歷每一個字符,如果遇到了符號位,我們看num的值,如果num是-1的話,說明是初始值,沒有更新過,我們將其賦值為0;反之,如果不是-1,說明num已經更新過了,我們乘上當前的正負符號值sign。這是為了區分"-3"和"3+3"這種兩種情況,遇到-3種的符號時,我們還不需要加到b中,所以num此時必須為0,而遇到3+3中的加號時,此時num已經為3了,我們要把前面的3加到b中。
遇到數字的時候,我們還是要看num的值,如果是初始值,那么就將其賦值為0,然后計算數字的時候要先給num乘10,再加上當前的數字。這樣做的原因是常數不一定都是個位數字,有可能是兩位數或者三位數,這樣做才能正確的讀入數字。我們在遇到數字的時候並不更新a或者b,我們只在遇到符號位或者x的時候才更新。這樣如果最后一位是數字的話就會產生問題,所以我們要在字符串的末尾加上一個+號,這樣確保了末尾數字會被處理。
遇到x的時候比較tricky,因為可能是x, 0x, -x這幾種情況,我們還是首先要看num的值是否為初始值-1,如果是的話,那么就可能是x或-x這種情況,我們此時將num賦值為sign;如果num不是-1,說明num已經被更新了,可能是0x, -3x等等,所以我們要將num賦值為num*sign。這里應該就明白了為啥不能將num初始化為0了,因為一旦初始化為0了,就沒法區分x和0x這兩種情況了。
那么我們算完了a和b,得到了ax=b的等式,下面的步驟就很簡單了,只要分情況討論得出正確的返回結果即可,參見代碼如下:
解法一:
class Solution { public: string solveEquation(string equation) { int a = 0, b = 0; auto found = equation.find("="); helper(equation.substr(0, found), true, a, b); helper(equation.substr(found + 1), false, a, b); if (a == 0 && a == b) return "Infinite solutions"; if (a == 0 && a != b) return "No solution"; return "x=" + to_string(b / a); } void helper(string e, bool isLeft, int& a, int& b) { int sign = 1, num = -1; e += "+"; for (int i = 0; i < e.size(); ++i) { if (e[i] == '-' || e[i] == '+') { num = (num == -1) ? 0 : (num * sign); b += isLeft ? -num : num; num = -1; sign = (e[i] == '+') ? 1 : -1; } else if (e[i] >= '0' && e[i] <= '9') { if (num == -1) num = 0; num = num * 10 + e[i] - '0'; } else if (e[i] == 'x') { num = (num == -1) ? sign : (num * sign); a += isLeft ? num : -num; num = -1; } } } };
下面這種解法也很不錯,也是求ax=b等式中的a和b,但是沒有根據等號拆分為左右兩部分,而是用一個變量sign來控制是對a和b加還是減,這跟上面解法中的的sign不一樣。這里沒有專門管正負的變量,而是通過雙指針,指向數字的范圍,這個數字可以是x的系數,也可以是常量,可以帶着正負號,然后通過stoi函數將字符串直接轉為整型數,然后乘以sign加到a或b中。變量j會指向數字或者符號,當i大於j時,我們就提取出范圍內的數字。當我們遇到x的時候,跟上面一樣,要處理+x, -x, 0x的情況,我們看前一位的字符,如果是符號,那么我們直接給a加上符號值;如果是數字,就用上面的辦法提取出數字乘以sign加到a中。如果遇到了等號,那么先處理前面的數字(如果有的話),然后flip sign。最后循環結束后,還要考慮最后一位是數字的情況,要加到b中。后面分情況討論就不多說了,參見代碼如下:
解法二:
class Solution { public: string solveEquation(string equation) { int n = equation.size(), a = 0, b = 0, sign = 1, j = 0; for (int i = 0; i < n; ++i) { if (equation[i] == '+' || equation[i] == '-') { if (i > j) b += stoi(equation.substr(j, i - j)) * sign; j = i; } else if (equation[i] == 'x') { if (i == j || equation[i - 1] == '+') { a += sign; } else if (equation[i - 1] == '-') { a -= sign; } else { a += stoi(equation.substr(j, i - j)) * sign; } j = i + 1; } else if (equation[i] == '=') { if (i > j) b += stoi(equation.substr(j, i - j)) * sign; sign = -1; j = i + 1; } } if (j < n) b += stoi(equation.substr(j)) * sign; if (a == 0 && a == b) return "Infinite solutions"; if (a == 0 && a != b) return "No solution"; return "x=" + to_string(-b / a); } };
類似題目:
Fraction Addition and Subtraction
參考資料:
https://discuss.leetcode.com/topic/95203/concise-java-solution
https://discuss.leetcode.com/topic/95279/c-two-pointers-concise-solution