Tính biểu thức đơn giản cho bằng xâu

Em/mình có bài tập như này. Đã giải bằng cách duyệt xâu nhưng khá dài dòng. Xin ý tưởng khác của mọi người.

Cho một xâu kí tự biểu diễn một biểu thức chỉ gồm phép cộng và phép trừ. Bạn hãy viết chương trình tính kết quả của biểu thức đó.

Dữ liệu đầu vào từ bàn phím gồm một dòng duy nhất chứa một biểu thức cần tính toán (độ dài biểu thức không vượt quá 100, giá trị của biểu thức và các số xuất hiện trong biểu thức không vượt quá 10^9 ). Ví dụ 1+2+3+4+5. ouput = 15.

Mình thì mình cũng làm thế thôi chứ làm thế nào :smiley: gặp dấu thì ngừng đọc số, đọc dấu đằng trước, rồi tính.

1 Like

Không duyệt thì làm sao mà biết nó có gì, duyệt!.

#include < iostream >
#include < string >
#include < cmath >
using namespace std;

int convert(string s)
{
    int x=0, n=s.length();
    for(int i=1; i<n; i++)
    {
       x=x*10;
       x=x+s[i]-48;
    }
    if(s[0]=='+') return x;
    if(s[0]=='-') return -x;
}

int node(string s)
{
    int x=0, n=s.length();
    for(int i=0; i<n; i++)
    {
       x=x*10;
       x=x+s[i]-48;
    }
    return x;
}
int main()
{
    string s; cin >> s;
    int result=0, i, j, k=1, temp=1;
    int number[100]={0}; int n=s.length();
    for(int i=0; i<n; i++)
        if(s[i]=='+' || s[i]=='-')
        {
            number[0]=node(s.substr(0, i)); break; // tính riêng phần tử đầu tiên
        }
    for(i=0; i<n-1; i++)
        for(j=i+1; j<n; j++)
        if((s[i]=='+' || s[i]=='-') && (s[j]=='+' || s[j]=='-'))
        {
            number[k++]=convert(s.substr(i, j-i));
            temp++;
            i=j;
        }
    for(int i=n-1; i>=0; i--)
        if(s[i]=='+' || s[i]=='-')
        {
            number[temp+1]=node(s.substr(i+1, n-1-i)); break; // phần tử cuối
        }
    for(int k=0; k<=temp+1; k++) result+=number[k];
    cout << result;
    return 0;
}
// Mọi người xem giúp code của em/ mình cần tối ưu chỗ nào
1 Like

Tối ưu đầu tiên là định dạng cho đẹp cái đã.

2 Likes

Bao trong cặp ``` như này:

Xem hướng dẫn: http://daynhauhoc.com/t/112

vâng, em vừa sửa rồi

Không cần thiết phải tạo ra xâu con + suy nghĩ theo kiểu state machine :smiley:

2 Likes

Cứ duyệt tới luôn, gặp số thì gán số, gặp dấu thì tính với số phía sau, chỉ cần (duy nhất) 1 vòng lặp là đủ.

Mã trên của bạn thích hợp trong trường hợp liệt kê tất cả số hạngkết quả. Còn đề bài chỉ yêu cầu kết quả nên chỉ cần 1 vòng lặp là đủ.

em chưa hiểu ý a lắm. thực ra là chắc là e không nghĩ ra nên mới chỉ nghĩ đc 2 vòng for

  1. Lấy số đầu tiên (cho đến khi gặp dấu) => kết quả tạm là số này.
  2. Gặp dấu => biết phép tính.
  3. Lấy số tiếp theo (cho đến khi gặp dấu) => dựa vào phép tính, kết quả tạm là phép tính với số tiếp theo này.
  4. Lặp lại 2. - 3. (cho đến hết chuỗi).

Dùng vài biến tạm để lưu kết quả, số hiện thời, phép tính…

Gợi ý:
Phép cộng: 1
Phép trừ: -1

a + b = a + (1)*b
a - b = a + (-1)*b
1 Like

Nhưng ý em là làm sao dùng 1 vòng for mà biết duyệt tới dấu thì dừng lại và lấy giá trị của số trước dấu đó. vì em chỉ biết dùng 2 for để tìm vị trí của nó trong xâu( xác định 2 dấu và số nằm ở giữa)

Mình thấy bạn đọc for i rồi s[i] rầm rầm cơ mà :smiley:

Có hai trạng thái là chữ số và phép tính. Sau chữ số có thể là chữ số hay +/-. Sau +/- phải là một chữ số :smiley: vậy khi bắt được +/- cũng là lúc tính toán.

1 Like

em nó đã giải rồi nên post code cũng ko sao nha :V

có thêm 1 trường hợp nhỏ nữa là dấu - ở kí tự đầu tiên ví dụ “-1+2”

#include <cctype>
#include <iostream>
#include <string>

long long eval(const std::string& s)
{
    long long ret = 0, token = 0;
    bool isAdd = s[0] != '-';
    for (int i = !isAdd; i < s.size(); ++i)
        if (isdigit(s[i]))
            token = 10 * token + s[i] - '0';
        else if (s[i] == '+' || s[i] == '-')
            ret += (isAdd ? token : -token), token = 0, isAdd = s[i] != '-';
    return ret += isAdd ? token : -token;
}

int main()
{
    std::cout << eval("-1+2+3-4+5-9") << "\n";
}
2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?