【问题标题】:How to rearrange a string equation?如何重新排列字符串方程?
【发布时间】:2018-10-05 16:06:31
【问题描述】:

我需要开发一个程序来求解线性方程。节目 首先读取一个整数 n,它是方程的数量。 然后程序读取包含方程的 n 行。 例如,程序的输入是这样的:

3
2x1+3x2+4x3=16
1x1+2x2+1x3=8
3x1+1x2+2x3=13

任何操作都应该首先将每个方程转换为 适当的形式。方程本身应具有以下性质

  1. 变量从左到右按字母顺序排列:

    3x2+2x1+4x3=16
    

    应该是

    2x1+3x2+4x3=16
    
  2. 任何变量都应该只出现一次:

    4x1+3x2-2x1+4x3=16
    

    应该是

    2x1+3x2+4x3=16
    
  3. 方程中只应出现一个常数项,它应该是 右侧:

    2x1+3x2+5+4x3-11=10
    

    应该是

    2x1+3x2+4x3=16
    
  4. 系数等于一或-1时,数字1是可选的:

    1x1+3x2-1x3=10
    

    可以原样输入

    x1+3x2-x3=10
    

到目前为止我所做的如下:

#include<iostream>
#include<string>
#include<sstream>
#include<cstdlib>

using namespace std;

int main() {
    int n;
    cin >> n;
    string eqn[100];
    //get eq from user
    for (int i = 0; i < n; i++) {
        cin >> eqn[i];
    }

    size_t  s = 0;
    size_t  y = 0;
    for (int i = 0; i < n; i++) {
        for (int x = 1; x <= ((eqn[i].length() - ((eqn[i].length() - 3) / 4)) / 3); x++)
        {
            int counter = 0;
            ostringstream ss;
            ss << x;
            string j = ss.str();
            for (int t = 0; t < eqn[i].length(); t++) {
                y = eqn[t].find("x" + j, y + 1);
                if (y < eqn[i].length()) { counter++; }
            }
            for (int o = 1; o <= counter; o++) {
                s = eqn[i].find("x" + j, s + 1);
                string x1 = eqn[i].substr(s - 1, 3);
                string x2 = x2 + x1;
                cout << x1;


            }


        }
        cout << endl;
    }
    int k;  cin >> k;

    return 0;
}

但事情变得过于复杂,我不确定这是否是正确的方法..

除了find()、substr()之外,还有更好的方法来操作字符串方程吗? 我应该如何解决这个问题?

【问题讨论】:

  • 听起来像家庭作业?
  • 这是我负责的大学项目,但我被卡住了,不知道有更好的方法来解决这个问题。
  • 使用find()、substr() 这样做可能是可行的,但很乏味。编写语法和编写解析器听起来更容易(对我来说),但如果您从未听说过编译器构造或形式语言,这也可能是一个挑战。我只是推荐其他有类似问题的人看看Syntax Diagram。如果您设法制作了这样的语法图,那么将其翻译成带有ifs、switchs 和for-loops 的手写解析器可能并不难。

标签: c++ string


【解决方案1】:

我从Syntax Diagram 开始定义(我不会称之为)一种语言:

然后我把它翻译成一个手写的解析器。

parse-equation.cc:

#include <iostream>
#include <algorithm>

int parseDigit(const char *&la)
{
  switch (*la) {
    case '0': ++la; return 0;
    case '1': ++la; return 1;
    case '2': ++la; return 2;
    case '3': ++la; return 3;
    case '4': ++la; return 4;
    case '5': ++la; return 5;
    case '6': ++la; return 6;
    case '7': ++la; return 7;
    case '8': ++la; return 8;
    case '9': ++la; return 9;
    default: return -1; // ERROR!
  }
}

int parseNumber(const char *&la)
{
  int value = parseDigit(la);
  if (value < 0) return -1; // ERROR!
  for (;;) {
    const int digit = parseDigit(la);
    if (digit < 0) return value;
    value *= 10; value += digit;
  }
}

struct Term {
  int coeff; // -1 ... missing
  int expon; // -1 ... missing -> ERROR

  Term(int coeff = -1, int expon = 0): coeff(coeff), expon(expon) { }
};

Term parseTerm(const char *&la)
{
  Term term;
  term.coeff = parseNumber(la);
  if (*la == 'x') {
    ++la;
    term.expon = parseDigit(la);
    if (term.coeff < 0) term.coeff = 1; // tolerate missing coeff. for x
  }
  return term;
}

struct Expression {
  bool error;
  int coeffs[10];

  Expression(bool error = false): error(error)
  {
    std::fill(std::begin(coeffs), std::end(coeffs), 0);
  }
};

Expression parseExpression(const char *&la)
{
  Expression expr;
  int sign = +1;
  do {
    const Term term = parseTerm(la);
    if (term.expon < 0) return Expression(true); // ERROR!
    expr.coeffs[term.expon] += sign * term.coeff;
    switch (*la) {
      case '+': sign = +1; ++la; break;
      case '-': sign = -1; ++la; break;
      case '=': break;
      default: return Expression(true); // ERROR!
    }
  } while (*la != '=');
  ++la;
  // parse right hand side
  const int result = parseNumber(la);
  if (result < 0) return Expression(true); // ERROR!
  expr.coeffs[0] -= result;
  // check for extra chars
  switch (*la) {
    case '\n': ++la;
    case '\0': break;
    default: return Expression(true); // ERROR!
  }
  return expr;
}

std::ostream& operator<<(std::ostream &out, const Expression &expr)
{
  if (expr.error) out << "ERROR!";
  else {
    bool empty = true;
    for (size_t i = 9; i; --i) {
      const int coeff = expr.coeffs[i];
      if (coeff) out << coeff << 'x' << i << std::showpos, empty = false;
    }
    if (empty) out << 0;
    out << std::noshowpos << '=' << -expr.coeffs[0];
  }
  return out;
}

int main()
{
  const char *samples[] = {
    "2x1+3x2+4x3=16",
    "1x1+2x2+1x3=8",
    "3x1+1x2+2x3=13",
    "2x1+3x2+5+4x3-11=10",
    "x1+3x2-x3=10"
  };
  enum { nSamples = sizeof samples / sizeof *samples };
  for (size_t i = 0; i < nSamples; ++i) {
    std::cout << "Parse '" << samples[i] << "'\n";
    const char *la = samples[i];
    std::cout << "Got    " << parseExpression(la) << std::endl;
  }
  return 0;
}

使用g++ 编译并在cygwin 中测试:

$ g++ -std=c++11 -o parse-equation parse-equation.cc 

$ ./parse-equation
Parse '2x1+3x2+4x3=16'
Got    4x3+3x2+2x1=16
Parse '1x1+2x2+1x3=8'
Got    1x3+2x2+1x1=8
Parse '3x1+1x2+2x3=13'
Got    2x3+1x2+3x1=13
Parse '2x1+3x2+5+4x3-11=10'
Got    4x3+3x2+2x1=16
Parse 'x1+3x2-x3=10'
Got    -1x3+3x2+1x1=10

$

Life Demo on Coliru

注意:

  1. 可以使用std::strtol() 代替parseDigit() 和parseNumber()。这将大大减少代码。

  2. 我使用const char* 表示“读取头”la(... 缩写为“向前看”)。纯粹的 C++ 方式可能是 std::stringstream 或 std::string::iterator,但是,我可能对这些新奇的东西还不够习惯。对我来说,const char* 是最直观的方式...

  3. 只需从 x0 的系数中减去右侧的结果。因此,要么右手边为 0,要么 x0 的负系数变为右手边。对于我漂亮的打印operator&lt;&lt;(),我选择了后一个选项。

  4. 错误处理相当差,可以通过有关解析失败原因的更详细信息来增强。我忽略了这一点,以免进一步“破坏”代码。

  5. 可以轻松增强解析器以跳过任何适当位置的空白。这将提高便利性。

  6. 在当前状态下,右侧的结果可能不是负数。我把这个扩展作为练习。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-28
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    相关资源
    最近更新 更多