Большинство программистов на C/C++ знают об отличных возможностях, предоставляемых директивами препроцессора #define, но также многим известны и проблемы, возникающие при их неаккуратном использовании.
В данной задаче мы рассматриваем следующую модель конструкций #define (также называемых макросами). Каждый макрос имеет свои имя и значение. Объявляется он следующим образом:
#define имя_макроса значение_макроса
После этого объявления везде, где в программе встречается слово "имя_макроса" (как отдельный токен, т.е. как подстрока, окружённая неалфавитными символами), оно заменяется на "значение_макроса". В "значение_макроса" в рамках нашей модели может быть записано только какое-либо арифметическое выражение, содержащее переменные, четыре арифметических операции, скобки, а также имя ранее объявленных макросов (в этом случае замена производится по цепочке). Процесс замены макросов на их значения называется подстановкой.
Одна из основных проблем, возникающих при использовании макросов — когда после выполнения подстановки получается арифметическое выражение, в котором из-за разных приоритетов операций может неожиданно измениться порядок вычисления.
Рассмотрим это на следующем примере. Определим такую конструкцию #define:
#define sum x + y
и пусть далее в программе считается выражение "2 * sum". После подстановки макроса получится выражение "2 * x + y", вместо интуитивно ожидаемого "2 * (x + y)".
Определим "подозрительной" ситуацию, когда после выполнения подстановки макроса порядок вычислений меняется, выходя за пределы какого-либо макроса. Соответственно, Ваша задача — по набору определений #define и заданному выражению определить, является это выражение подозрительным или нет.
Определим это более формально. Выполним обычную подстановку макросов в заданном выражении. Кроме того, выполним "безопасную" подстановку макросов в выражение: окружив значение каждого макроса скобками; после этого, пользуясь арифметическими правилами раскрытия скобок, можно опустить некоторые скобки. Если при этом можно получить выражение, полностью совпадающее с результатом обычной подстановки (посимвольно, но игнорируя пробелы), то это выражение и система макросов считаются корректными, иначе — подозрительными.
Примечание. В этом критерии операция деления рассматривается с математической точки зрения, а не в смысле языка C++ (в котором под ним подразумевается деление нацело). Например, в выражении "a*(b/c)" мы можем опустить скобки и получить выражение "a*b/c".