cppAcademy
FROM LEARNING TO COMPETING

Expert.
Put your skills to the test.

Bulgarian Informatics Group A · 2009–2026

217 independently explained problems: 54 Municipal, 54 Regional and 109 National. Start with the easier tasks and work upwards. Difficulty labels are learning estimates, not official ratings; Group A also includes challenging national-level problems. The 109 National-round tasks shared by Groups A and B retain their existing permanent URLs and explanations. Years follow Arena’s competition-season labels. Source clarifications and special grader requirements are noted on affected tasks.
GROUP A2016 · MediumParsing

Evaluate NAND Expressions

National Olympiad — Regional round (2016 season)

Submission name: expr.cpp

Problem summary

Evaluate a parenthesized expression under multiple Boolean assignments. Adjacent expressions are combined by NAND: the result is zero only when both arguments are one. Without parentheses operations associate from left to right. Variables are precisely the first k lowercase letters.

Input

The expression, followed by distinct k-bit assignments, terminated by a line containing 2.

Output

One line concatenating the expression values in assignment order.

Constraints

Expression length ≤ 1000; at least 1 and at most 200000 assignments; at most 26 variables.

Worked example

Official sample; other valid outputs may exist.

Input

a(a)(ab(ca)bb)c
011
100
010
111
001
2

Output

01101

Solution analysis

  1. Parse the expression once into postfix instructions. Each parenthesis level contains a left-to-right chain: after parsing an atom, emit NAND after every following atom. Parenthesized atoms recursively emit their own instructions.
  2. For each assignment execute the postfix program with a Boolean stack. A variable pushes its assigned bit. NAND pops two bits and pushes the negation of their conjunction. This avoids reparsing for every query.
  3. For an unparenthesized abc, postfix is ab#c#, where # means NAND. With a=b=c=1, the first NAND gives 0 and the second gives 1; this differs from right association.

Why it works

Structural induction proves that parsing each atom emits its value, and the chain parser emits the specified left fold. Standard stack evaluation computes each emitted operation on the two corresponding subexpression values. Thus every reported bit equals the defined expression value.

Complexity: O(LQ) time and O(L) working space, with L expression length and Q assignments.

Watch out. NAND is not associative. Parentheses around one variable do not add an operation.

Commented C++17 solution

An independently written complete program with standard headers.

// Key idea: Structural induction proves that parsing each atom emits its value, and the chain parser
// emits the specified left fold. Standard stack evaluation computes each emitted operation on the two
// corresponding subexpression values.
#include<iostream>
#include<string>
#include<vector>
using namespace std;
string expression,program;
size_t position=0;
void parseChain();
void parseAtom(){
    if(expression[position]=='('){
        ++position;
        parseChain();
        ++position;
    }
    else{
        program+=expression[position++];
    }
}
void parseChain(){
    parseAtom();
    while(position<expression.size()&&expression[position]!=')'){
        parseAtom();
        program+='#';
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>expression;
    parseChain();
    string assignment;
    vector<int>stack(program.size());
    while(cin>>assignment&&assignment!="2"){
        int size=0;
        for(char instruction:program){
            if(instruction!='#'){
                stack[size++]=assignment[instruction-'a']-'0';
            }
            else{
                int right=stack[--size];
                stack[size-1]=!(stack[size-1]&&right);
            }
        }
        cout<<stack[0];
    }
    cout<<'\n';
}

Source: Arena Olimpiici ↗