Advertisement
giGii

domains

Apr 27th, 2023 (edited)
554
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.83 KB | None | 0 0
  1. #include <algorithm>
  2. #include <iostream>
  3. #include <string>
  4. #include <sstream>
  5. #include <string_view>
  6. #include <vector>
  7. #include <unordered_map>
  8. #include <cmath>
  9. #include <cassert>
  10.  
  11. using namespace std;
  12.  
  13. // узел префиксного дерева, https://ru.algorithmica.org/cs/string-structures/trie/
  14. struct Node {
  15.     unordered_map<char, Node*> childs;
  16. };
  17.  
  18. class Domain {
  19. public:
  20.     explicit Domain(const string& domain) {
  21.         domain_ = {domain.crbegin(), domain.crend()};
  22.         // точка в начале домена нужна на такой случай запрещенных доменов: cpp.ru и p.ru
  23.         // она разделит эти случаи на ветки: а) u--r--.--p--. б) u--r--.--p--p--c--.
  24.         // иначе же будет одна ветка u--r--.--p--p--c
  25.         domain_.push_back('.');
  26.     }
  27.  
  28.     bool operator==(const Domain& other) const {
  29.         return domain_ == other.domain_;
  30.     }
  31.  
  32.     bool operator<(const Domain& other) const {
  33.         return domain_ < other.domain_;
  34.     }
  35.  
  36.     // Принимает другой домен и возвращающий true, если this его поддомен
  37.     bool IsSubdomain(const Domain& other) const {
  38.         const string& other_domain = other.Get();
  39.  
  40.         if (domain_.size() > other_domain.size()) {
  41.             // не может this быть поддоменом, если this длиннее
  42.             return false;
  43.         }
  44.        
  45.         // если this не длиннее, значит он меньше или равен,
  46.         // тогда обрежем other по this
  47.         const string new_other = other.domain_.substr(0, domain_.size());
  48.        
  49.         return domain_ == new_other;
  50.     }
  51.  
  52.     const string& Get() const {
  53.         return domain_;
  54.     }
  55.  
  56. private:
  57.     string domain_ = ""s;
  58. };
  59.  
  60. class DomainChecker {
  61. public:
  62.     // конструктор принимает список запрещённых доменов через пару итераторов
  63.     template<typename InputIt>
  64.     explicit DomainChecker(InputIt first, InputIt last) {
  65.         vector<Domain> processed_domains = {first, last};
  66.         sort(processed_domains.begin(), processed_domains.end());
  67.  
  68.         // в запрещенных доменах префиксного дерева плоха та ситуация, когда
  69.         // один домен есть поддоммен другого домена; например, когда:
  70.         // 1) cpp.ru и ru -- эта ситуация исключается unique
  71.         // 2) cpp.ru и p.ru -- эта ситуация исключается вставкой точки в начало домена
  72.  
  73.         // если ситуацию 1) не обработать, нельзя будет понять для сайта
  74.         // python.ru запрещен ли он, потому что нельзя понять:
  75.         // - ru это один из запрещенных доментов?
  76.         // - или это часть запрещенного домена?
  77.         // если ситуацию 2) не предусмотреть, в дереве запрещенной будет только одна
  78.         // ветка u--r--.--p--p--c-- вместо двух положенных веток
  79.               // u--r--.--p--p--c--.
  80.               // u--r--.--p--.
  81.  
  82.         auto last_unique = unique(processed_domains.begin(), processed_domains.end(),
  83.                             [](const Domain& lhs, const Domain& rhs) {
  84.                                 return lhs.IsSubdomain(rhs);
  85.                             });
  86.        
  87.         root_ = new Node();
  88.  
  89.         for (auto domain_it = processed_domains.begin(); domain_it != last_unique; ++domain_it) {
  90.             Node* tmp_root = root_;
  91.             const string& domain_name = domain_it->Get();
  92.  
  93.             for (auto char_it = domain_name.cbegin(); char_it != domain_name.cend(); ++char_it) {
  94.                 char c = *char_it;
  95.                 if (tmp_root->childs.count(c) == 0) {
  96.                     auto node = new Node;
  97.                     all_nodes_to_delete_.push_back(node);
  98.  
  99.                     tmp_root->childs[c] = node;
  100.                     tmp_root = node;
  101.                 } else {
  102.                     tmp_root = tmp_root->childs[c];
  103.                 }
  104.             }
  105.         }
  106.     }
  107.  
  108.     // удалит все узлы, созданные в куче
  109.     ~DomainChecker() {
  110.         for (Node* node_to_delete : all_nodes_to_delete_) {
  111.             delete node_to_delete;
  112.         }
  113.  
  114.         delete root_;
  115.     }
  116.  
  117.     // IsForbidden, вернет true, если домен запрещён
  118.     bool IsForbidden(const Domain& domain) {
  119.  
  120.         // запрещенных доменов нет, значит ничто не запрещено
  121.         if (root_->childs.size() == 0) {
  122.             return false;
  123.         }
  124.         // домен domain будет запрещенным, если какой-то запрещенный домен является его суффиксом, например:
  125.         // запрещенный рф.ru и домены one.рф.ru, two.рф.ru, но не
  126.         // запрещенный рф.ru и домены oneрф.ru, twoрф.ru
  127.  
  128.         const string& domain_to_check = domain.Get();
  129.         Node* tmp_root = root_;
  130.  
  131.         // проверяем, есть ли цепочка символов domain_to_check в какой-то ветке дереве запрещенных доменов
  132.         for (auto it = domain_to_check.begin(); it != domain_to_check.end(); ++it) {
  133.  
  134.             if (tmp_root->childs.size() == 0) {
  135.                 // ветка дерева закончилась, значит она является суффиксом проверяемого домена
  136.                 return true;
  137.             } else if (tmp_root->childs.count(*it) == 0) {
  138.                 // символа из проверяемого домена нет в ветке дерева
  139.                 return false;
  140.             }
  141.  
  142.             // символ из проверяемого домена есть в подходящем запрещенном домене,
  143.             // берем детей этого символа и на следующей итерации история повторится
  144.             tmp_root = tmp_root->childs.at(*it);
  145.         }
  146.        
  147.         if (tmp_root->childs.size() > 0) {
  148.             return false;
  149.         }
  150.  
  151.         return true; // эквивалентно tmp_root->childs.size() == 0
  152.     }
  153.  
  154.     // сделан для тестирования
  155.     bool IsForbidden(const string& domain) {
  156.         return IsForbidden(Domain{domain});
  157.     }
  158.  
  159. private:
  160.     Node* root_;
  161.     vector<Node*> all_nodes_to_delete_;
  162. };
  163.  
  164. // ReadNumberOnLine считывает число из строки
  165. template <typename Number>
  166. Number ReadNumberOnLine(istream& input) {
  167.     string line;
  168.     getline(input, line);
  169.  
  170.     Number num;
  171.     std::istringstream(line) >> num;
  172.  
  173.     return num;
  174. }
  175.  
  176. // ReadDomains читает заданное количество доменов из стандартного входа
  177. vector<Domain> ReadDomains(istream& input, size_t lines) {
  178.     string domain;
  179.     vector<Domain> forbidden_domains;
  180.    
  181.     for (size_t line = 0; line < lines; ++line) {
  182.         getline(input, domain);
  183.         forbidden_domains.emplace_back(domain);
  184.     }
  185.  
  186.     return forbidden_domains;
  187. }
  188.  
  189. void YandexTest() {
  190.     stringstream stream;
  191.  
  192.     stream << "4\n"s
  193.               "gdz.ru\n"
  194.               "maps.me\n"
  195.               "m.gdz.ru\n"
  196.               "com\n";
  197.  
  198.     const std::vector<Domain> raw_forbidden_domains = ReadDomains(stream, ReadNumberOnLine<size_t>(stream));
  199.     DomainChecker checker(raw_forbidden_domains.begin(), raw_forbidden_domains.end());
  200.  
  201.     assert(checker.IsForbidden("gdz.ru"s));
  202.     assert(checker.IsForbidden("gdz.com"s));
  203.     assert(checker.IsForbidden("m.maps.me"s));
  204.     assert(checker.IsForbidden("alg.m.gdz.ru"s));
  205.     assert(checker.IsForbidden("maps.com"s));
  206.     assert(!checker.IsForbidden("maps.ru"s));
  207.     assert(!checker.IsForbidden("gdz.ua"s));
  208. }
  209.  
  210. void MyTest() {
  211.     stringstream stream;
  212.  
  213.     stream << "8\n"
  214.               "js.fr\n"
  215.               "ts.js.fr\n"
  216.               "com\n"
  217.               "hello.com\n"
  218.               "abacaba.rf\n"
  219.               "hello.world\n"
  220.               "y.sva.yl.mvs.cp.yww\n" // хитрый тест
  221.               "hsr.oxay.sva.yl.mvs.cp.yww\n"; // если этот домен запрещен
  222.    
  223.     const std::vector<Domain> raw_forbidden_domains = ReadDomains(stream, ReadNumberOnLine<size_t>(stream));
  224.     DomainChecker checker(raw_forbidden_domains.begin(), raw_forbidden_domains.end());
  225.  
  226.     // проверка, что сами запрещенные домены являются запрещенными
  227.     assert(checker.IsForbidden("js.fr"s));
  228.     assert(checker.IsForbidden("ts.js.fr"s));
  229.     assert(checker.IsForbidden("com"s));
  230.     assert(checker.IsForbidden("hello.com"s));
  231.     assert(checker.IsForbidden("abacaba.rf"s));
  232.     assert(checker.IsForbidden("hello.world"s));
  233.     assert(checker.IsForbidden("y.sva.yl.mvs.cp.yww"s)); // хитрый тест
  234.  
  235.     // проверка, что их поддомены являются запрещенными
  236.     assert(checker.IsForbidden("ans.com"s));
  237.     assert(checker.IsForbidden("hello.yandex.com"s));
  238.     assert(checker.IsForbidden("some.subdomain.for.js.fr"s));
  239.     assert(checker.IsForbidden("baca.baca.baca.baca.baca.abacaba.rf"s));
  240.     assert(checker.IsForbidden("good-morning.tonight.yello.hello.world"s));
  241.  
  242.     // проверка, что НЕ поддомены не являются запрещенными
  243.     assert(!checker.IsForbidden("s.fr"s));
  244.     assert(!checker.IsForbidden("csharp.fr"s));
  245.     assert(!checker.IsForbidden("ans.dotcom"s));
  246.     assert(!checker.IsForbidden("acaba.rf"s));
  247.     assert(!checker.IsForbidden("caba.rf"s));
  248.     assert(!checker.IsForbidden("_ello.world"s));
  249. }
  250.  
  251. void SubDomainTest() {
  252.     assert(Domain{"ru"s}.IsSubdomain(Domain{"abc.ru"s}));
  253.     assert(Domain{"abc.ru"s}.IsSubdomain(Domain{"abc.ru"s}));
  254.     assert(!Domain{"abc.ru"s}.IsSubdomain(Domain{"ru"s}));
  255.  
  256.     assert(!Domain{"com"s}.IsSubdomain(Domain{"ans.dotcom"s}));
  257.  
  258.     assert(Domain{"com"s}.IsSubdomain(Domain{"ans.com"s}));
  259. }
  260.  
  261. void OperatorEqualDomainTest() {
  262.     assert(Domain{"afgbjsbfkjhblhblfh"s} == Domain("afgbjsbfkjhblhblfh"s));
  263.     assert(Domain{"rf.rf.rf"s} == Domain("rf.rf.rf"s));
  264.     assert(!(Domain{"hello.rf"s} == Domain("hello.rt"s)));
  265. }
  266.  
  267. void ReadDomainsTest() {
  268.     stringstream stream;
  269.  
  270.     stream << "6\n"
  271.               "js.fr\n"
  272.               "ts.js.fr\n"
  273.               "com\n"
  274.               "hello.com\n"
  275.               "abacaba.rf\n"
  276.               "hello.world\n";
  277.    
  278.     const std::vector<Domain> domains = ReadDomains(stream, ReadNumberOnLine<size_t>(stream));
  279.  
  280.     assert(domains[0] == Domain{"js.fr"s});
  281.     assert(domains[1] == Domain{"ts.js.fr"s});
  282.     assert(domains[2] == Domain{"com"s});
  283.     assert(domains[3] == Domain{"hello.com"s});
  284.     assert(domains[4] == Domain{"abacaba.rf"s});
  285.     assert(domains[5] == Domain{"hello.world"s});
  286. }
  287.  
  288. void Test() {
  289.     ReadDomainsTest();
  290.     OperatorEqualDomainTest();
  291.     SubDomainTest();
  292.     YandexTest();
  293.     MyTest();
  294.  
  295.     // cout << __PRETTY_FUNCTION__ << " OK"sv << endl;
  296. }
  297.  
  298. int main() {
  299.     Test();
  300.  
  301.     const std::vector<Domain> raw_forbidden_domains = ReadDomains(cin, ReadNumberOnLine<size_t>(cin));
  302.  
  303.     DomainChecker checker(raw_forbidden_domains.begin(), raw_forbidden_domains.end());
  304.  
  305.     const std::vector<Domain> test_domains = ReadDomains(cin, ReadNumberOnLine<size_t>(cin));
  306.    
  307.     for (const Domain& domain : test_domains) {
  308.         cout << (checker.IsForbidden(domain) ? "Bad"s : "Good"s) << endl;
  309.     }
  310. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement