核心思路是代值,因为只有一个变量,代一个特殊值就可以将问题转化为表达式求值了。

求值的方法是将中缀表达式转化为计算机比较容易计算的后缀表达式(逆波兰表达式),需要使用两个栈分别存数字和符号,处理方法如下:

  1. 若取出的字符是数字,则分析出完整数字,该操作数直接送入数字栈。

  2. 若取出的字符是运算符,则将该运算符与符号栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于该栈顶运算符优先级,则将该运算符进该栈;否则,将该栈的栈顶运算符弹出,并将数字栈中的最后两个数字进行计算(我代码中的函数),直至符号栈栈顶运算符小于该运算符优先级,最后将该运算符送入符号栈。

  3. 若取出的字符是“”,则直接送入符号栈顶。

  4. 若取出的字符是“”,则将距离符号栈栈顶最近的“”之间的运算符,逐个出栈,并依次取出数字栈中的数字进行计算,最后抛弃“”。

  5. 重复上面的~步,直至处理完所有的输入字符。

比较坑人的点:

  • Linux中换行符是'\n',而windows中是'\r\n'(多一个字符),有些数据在windows中生成,而有些评测机(洛谷的)确是在Linux环境下评测,这种情况在字符串输入中非常常见。不过牛客的就不用考虑'\r'。我一开始用,0分了半天(运行时错误),心态十分爆炸,后面用了一个大佬的读入(自定义的函数)才过了。

  • 数据比较恶心,有括号没有匹配上的。请记得在取符号栈栈顶的之前一定要检查栈是不是空的。

  • 代值的数我使用,尽量用比较大的质数,模数我用

  • 计算只有加减乘和乘幂,没有除法,注意取模。我用的是,实际上用更好。

AC代码如下,略长,注释详细,请轻喷

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int level[200];//记录符号优先级,数字优先级为0
const int mod=1e9+7, x = 131;//取模的数,带入的值
stack<int>num;//数字栈
stack<char>opera;//符号栈
string s;

//最坑人的string字符串读入,getline全RE  ┭┮﹏┭┮
string read(){
    string ss;
    char ch = getchar();
    while(ch=='\n'||ch=='\r'||ch==' ') ch=getchar();//诡异回车符
    while(ch!='\n'&&ch!='\r'){
        if(ch!=' ') ss+=ch;//忽略空格
        ch=getchar();
        if(ch==EOF) break;
    }
    return ss;
}

//快速幂,返回n1的n2次幂,取模为1e9+7
int quickPower(int n1,int n2){
    ull ans = 1,pow = n1;
    while(n2){
        if(n2&1) ans = ans*pow%mod;
        pow = pow*pow%mod;
        n2>>=1;
    }
    return ans%mod;
}

//计算n1与n2关于op的运算(+-*^都是二元运算符)
void calculate(){
    int n1 = num.top();//取数字栈中的两个数,注意顺序是n2 (运算符) n1
    num.pop();
    int  n2 = num.top();//取数字栈中的两个数
    num.pop();
    int ans;
    switch (opera.top()) {
        case '+':ans = (n2+n1)%mod;  break;
        case '-':ans = (n2-n1+mod)%mod;  break;//防止出现负数
        case '*':ans = ( (long long)(n2%mod) * (n1%mod) )%mod; break;//要转成longlong,不然会爆int
        case '^':ans = quickPower(n2,n1);
    }
    num.push(ans);//将新数返回数字栈中
    opera.pop();//符号栈弹栈
}

//形成后缀表达式
void pushStack(){
    int len = (int)s.length(),sum = 0;
    for(int i = 0;i < len;i++){
        //要么是变量a,要么是数字,要么是运算符
        if(s[i] == 'a') num.push(x);//是未知数的话就带入值131
        else if(isdigit(s[i])){//处理数字
            sum = sum*10+(s[i]-'0');
            if(i == len-1||!isdigit(s[i+1]) ) num.push(sum),sum = 0;
            //如果这是最后一个数字,或者它后面已经不再是数字了,就进数字栈
        }
        //处理运算符
        else {
            if(s[i] == '(') {//左括号直接入栈
                opera.push('(');
                continue;
            }else if(s[i] == ')'){//右括号要不断弹出符号栈,直到遇到左括号
                while(!opera.empty() && opera.top() != '(') calculate();
                if(!opera.empty() && opera.top() == '(') opera.pop();//防止括号不匹配
                continue;
            }
            //+-*^的情况下,当前运算符等级比符号栈栈顶等级要高或者相等,就要先计算栈顶的运算符
            while(!opera.empty() && level[s[i]] <= level[opera.top()])  calculate();
            opera.push(s[i]);
        }
    }
}

int getResult(){
    pushStack();//形成后缀表达式
    while(!opera.empty()){
        if(opera.top()=='(') {//如果括号不匹配(多出括号的情况)
            opera.pop();continue;
        }
        calculate();
    }
    return num.top();
}

int main(){
    level['^'] = 4;level['*'] = 3;level['+'] = 2;level['-'] = 2;//初始化运算符优先级
    s = read();
    int n,ans;
    ans = getResult();//记录原表达式的值
    cin>>n;
    for(int i = 0;i < n;i++){
        while(!num.empty()) num.pop();//记得清空栈
        while(!opera.empty()) opera.pop();
        s = read();
        if(getResult() == ans) printf("%c",'A'+i);
    }
    return 0;
}