diff --git a/contests/10contest/2.cpp b/contests/10contest/2.cpp new file mode 100644 index 0000000..2f91d0d --- /dev/null +++ b/contests/10contest/2.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +void optimize(std::map>& map) { + std::set exist; + std::stack stack; + + + stack.push('S'); + exist.insert('S'); + + while (!stack.empty()) { + char now = stack.top(); + stack.pop(); + + for (const auto& str : map[now]) { + for (const auto& c : str) { + if (!('A' <= c && c <= 'Z')) { + continue; + } + if (exist.find(c) == exist.end()) { + exist.insert(c); + stack.push(c); + } + } + } + } + + auto it = map.begin(); + while (it != map.end()) { + if (exist.find(it->first) == exist.end()) { + it = map.erase(it); + } else { + ++it; + } + + } + + return; +} + +int main() { + // std::vector> mas; + std::map> map; + char left; + std::string right; + + while (std::cin >> left >> right) { + // mas.push_back(std::make_pair(left, right)); + map[left].push_back(right); + } + + optimize(map); + + // for (const auto& rule : mas) { + // if (map.find(rule.first) != map.end()) { + // std::cout << rule.first << " " << rule.second << std::endl; + // } + // } + + for (const auto& rules : map) { + for (const auto& rule : rules.second) { + std::cout << rules.first << " " << rule << std::endl; + } + } + + return 0; +} \ No newline at end of file diff --git a/contests/10contest/5.cpp b/contests/10contest/5.cpp new file mode 100644 index 0000000..b90b3ed --- /dev/null +++ b/contests/10contest/5.cpp @@ -0,0 +1,46 @@ +/* + На стандартный поток ввода программе подаётся выражение в польской записи. Выражение может содержать имена переменных и бинарные операции +, -, *, /. Имя переменной — это строчная латинская буква. Элементы ввода могут разделяться произвольным количеством пробельных символов. + +Напечатайте введённое выражение в инфиксной записи. Для сохранения приоритетов бинарные операции заключите в скобки. Выражение напечатайте в одной строке текста без пробельных символов. +Examples +Input + +abc++ + +Output + +(a+(b+c)) +*/ + +#include +#include +#include + +bool is_operation(char c) { + return c == '+' || c == '-' || c == '*' || c == '/'; +} + +int main() { + char c; + std::stack stack; + + while (std::cin >> c) { + if (is_operation(c)) { + std::string r = stack.top(); + stack.pop(); + + std::string l = stack.top(); + stack.pop(); + + stack.push("(" + l + c + r + ")"); + } else { + stack.push(std::string(1, c)); + } + } + + std::cout << stack.top() << std::endl; + + stack.pop(); + + return 0; +} diff --git a/lectures/LL1.cpp b/lectures/LL1.cpp new file mode 100644 index 0000000..71fb032 --- /dev/null +++ b/lectures/LL1.cpp @@ -0,0 +1,231 @@ +// Разбор журналов + +// Реализуйте класс CFGrammar, который используется для представления контекстно-свободной грамматики и вычисления множеств First и Follow для её нетерминалов. Терминалами грамматики считаются строчные символы латинского алфавита, а нетерминалами - заглавные символы латинского алфавита. + +// 1) У класса должен быть реализован метод add_rule(char, const std::string&) для добавления нового правила в грамматику. Первый аргумент представляет нетерминал, из которого выводится правило, и может иметь значения от "А" до "Z". Второй аргумент представляет само правило, выводимое из данного нетерминала и может быть строкой (возможно, пустой), состоящей из допустимых терминалов и нетерминалов. + +// 2) У класса должен быть реализован метод get_first(), который возвращает ассоциативный массив (map или unordered_map), ключами которого являются нетерминалы грамматики, а значениями - множество символов, входящих в First от этого нетерминала. Допустимыми элементами множества являются терминалы и специальный символ '_' для обозначения пустой цепочки (эпсилон). + +// 3) У класса должен быть реализован метод get_follow(), который возвращает ассоциативный массив (map или unordered_map), ключами которого являются нетерминалы грамматики, а значениями - множество символов, входящих в Follow от этого нетерминала. Допустимыми элементами множества являются терминалы и специальный символ '$' для обозначения конца строки. + +// 4) У класса должен быть реализован метод is_ll1(), который возвращает true, если грамматика удовлетворяет критерию LL(1) и false в противном случае. + +// После подстановки решения студента, следующий код должен работать и выдавать ожидаемый результат (см. пример ввода и вывода): + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool is_terminal(char c) { return c >= 'a' && c <= 'z'; } +bool is_non_terminal(char c) { return c >= 'A' && c <= 'Z'; } + +std::string set_to_canonical_string(const auto& set_of_chars, char special_symbol) { + std::string result; + bool has_special_symbol = false; + for (char c : set_of_chars) { + if (is_terminal(c)) + result += c; + else + has_special_symbol = true; + } + std::sort(result.begin(), result.end()); + if (has_special_symbol) + result += special_symbol; + return result; +} + +// Here goes your submission + +class CFGrammar { +private: + std::unordered_map> rules; + std::unordered_map> first; + std::unordered_map> follow; + + void compute_first() { + bool changed; + do { + changed = false; + for (const auto& [non_terminal, productions] : rules) { + for (const auto& production : productions) { + bool nullable = true; + for (char symbol : production) { + if (is_terminal(symbol)) { + if (first[non_terminal].insert(symbol).second) { + changed = true; + } + nullable = false; + break; + } else if (is_non_terminal(symbol)) { + for (char c : first[symbol]) { + if (c != '_' && first[non_terminal].insert(c).second) { + changed = true; + } + } + if (first[symbol].count('_') == 0) { + nullable = false; + break; + } + } + } + if (nullable && first[non_terminal].insert('_').second) { + changed = true; + } + } + } + } while (changed); + } + + void compute_follow() { + follow.begin()->second.insert('$'); + bool changed; + do { + changed = false; + for (const auto& [non_terminal, productions] : rules) { + for (const auto& production : productions) { + std::set trailer = follow[non_terminal]; + for (auto it = production.rbegin(); it != production.rend(); ++it) { + char symbol = *it; + if (is_non_terminal(symbol)) { + for (char c : trailer) { + if (follow[symbol].insert(c).second) { + changed = true; + } + } + if (first[symbol].count('_') == 1) { + trailer.insert(first[symbol].begin(), first[symbol].end()); + trailer.erase('_'); + } else { + trailer = first[symbol]; + } + } else if (is_terminal(symbol)) { + trailer = { symbol }; + } else { + trailer.clear(); + } + } + } + } + } while (changed); + } + +public: + void add_rule(char non_terminal, const std::string& production) { + rules[non_terminal].push_back(production); + } + + std::unordered_map> get_first() { + if (first.empty()) { + compute_first(); + } + return first; + } + + std::unordered_map> get_follow() { + if (follow.empty()) { + compute_first(); + compute_follow(); + } + return follow; + } + + bool is_ll1() { + for (const auto& [non_terminal, productions] : rules) { + std::set seen; + for (const auto& production : productions) { + std::set first_set; + bool nullable = true; + for (char symbol : production) { + if (is_terminal(symbol)) { + first_set.insert(symbol); + nullable = false; + break; + } else if (is_non_terminal(symbol)) { + first_set.insert(first[symbol].begin(), first[symbol].end()); + if (first[symbol].count('_') == 0) { + nullable = false; + break; + } + } + } + if (nullable) { + first_set.insert(follow[non_terminal].begin(), follow[non_terminal].end()); + } + for (char c : first_set) { + if (seen.count(c)) { + return false; + } + seen.insert(c); + } + } + } + return true; + } +}; + +int main() { + std::string line; + std::regex rule_regex("^([A-Z])\\s*->\\s*([a-zA-Z]*)$"); + + CFGrammar grammar; + while (std::getline(std::cin, line)) { + std::smatch m; + if (std::regex_match(line, m, rule_regex)) { + grammar.add_rule(m[1].first[0], m[2]); + } else { + break; + } + } + + std::vector> sorted_first; + for (auto& entry : grammar.get_first()) + sorted_first.emplace_back(entry.first, set_to_canonical_string(entry.second, '_')); + std::sort(sorted_first.begin(), sorted_first.end()); + + std::cout << "First:" << std::endl; + for (auto& entry : sorted_first) + std::cout << entry.first << ": " << entry.second << std::endl; + + std::vector> sorted_follow; + for (auto& entry : grammar.get_follow()) + sorted_follow.emplace_back(entry.first, set_to_canonical_string(entry.second, '$')); + std::sort(sorted_follow.begin(), sorted_follow.end()); + + std::cout << "Follow:" << std::endl; + for (auto& entry : sorted_follow) + std::cout << entry.first << ": " << entry.second << std::endl; + + std::cout << "is LL(1): " << (grammar.is_ll1() ? "Yes" : "No") << std::endl; + return 0; +} +/* +Примеры +Входные данные + +S -> ABC +A -> aA +A -> +B -> b +B -> C +C -> c + +Результат работы + +First: +A: a_ +B: bc +C: c +S: abc +Follow: +A: bc +B: c +C: c$ +S: $ +is LL(1): Yes +*/ \ No newline at end of file diff --git a/seminars/7.md b/seminars/7.md new file mode 100644 index 0000000..90f0d9c --- /dev/null +++ b/seminars/7.md @@ -0,0 +1,156 @@ +# Хуй знает что тут + +## Задания + +### Первое задание + +```plain +L1 = {1^n 0^m 1^m 0^n | n, m > 0} +r = {(), a^m b^(n+m)} // выдать на выходе вместо 1010 aabb +``` + +```plain +S - 1A0 +A - 1A0 | 0B1 +B - 0B1 | _ +``` + +Переводим в другую грамматику (в скобках моменты печати слова) + +```plain +S - 1A(b)0 +A - 1A(b)0 | 0(a)B(b)1 +B - 0(a)B(b)1 | _ +``` + +### Второе задание + +```plain +L1 = {(c/d)a^n b^n | n> 0} +r = {(L1), 1^2n 0^(k+n) | k = |d|} +``` + +Описание языка: + +```plain +S - cS | dS | aAb +A - aAb | _ +``` + +Преобразование + +```plain +S - cS | dS(0) | a(11)A(0)b +A - a(11)A(0)b | _ +``` + +### Третье задание + +```plain +S - bA +A - aC +B - aBb | _ +C - Bba | _ +``` + +```plain +r = {(), b^k a ^ m, k = |b|, m = |a|} +``` + +```plain +S - (b)bA +A - aC(a) +B - (b)aBb(a) | _ +C - (b)Bba(a) | _ +``` + +## Перевод в польско-инверсную запись + +```plain +E - T {+T} +T - F {*F<*>} +F - a|b|(E) +``` + +```cpp +void E() { + T(); + while(c == '+') {gc(); T(); cout << '+';} +} +``` + +из `a+a*b` получим `aab*+` + +### Задача + +(тут был граф) + +описывающая грамматика: + +```plain +S - ccCa +C - cCd | B +B - b +``` + +грамматика, которая перевернёт: + +```plain +S - ccCa +C - cCd | B +B - b +``` + +## Переводы записей + +```plain +1) s = i * (--a+b++) + +&s i &a -# &b #+ + * = ; + +``` + +```plain +2) x = (a + b, a * b); + +&x a b + ; a b * = ; +``` + +Переход в какое-то место программы: _,! + +B, _, !F - переход по лжи (B == False) + +```cpp +if (B) p1; else pe; +B 7 !F p1 8 ! p2 +1 2 3 4 5 6 7 8 +``` + +```cpp +1) if ((x+1) > y) x = y; else y = (x-y) * 3; +2) while (i < 9) {s = 3 + f(i), i++;} +3) do {x = z = (x + y + 5) * b - 3/f; b++;} while(-x+y>0); +4) for (i = 0; i < n; i++) {a = b - c;} +``` + +```plain +if ((x+1) > y) x = y; else y = (x-y) * 3; + +x 1 + y > !F 18 &x y = ; 22 ! &y x y - 3 * = ; +1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 +``` + +```plain +while (i < 9) {s = 3 + f(i), i++;} + +i 9 < 17 !F &s 3 i f = ; &i #+ ; 1 ! +1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +``` + +``` plain +do {x = z = (x + y + 5) * b - 3/f; b++;} while(-x+y>0); + + +&a a b < _ !F a _ ! b = ; + +```