/*/ problem source: * https://www.geeksforgeeks.org/find-the-longest-substring-with-k-unique-characters-in-a-given-string/ Possible references include: https://stackoverflow.com/a/7304184 - custom delimeter for istream */ #include #include #include #include #include #include #include struct semicolon_is_space : std::ctype { // this struct/class is adapted from: https://stackoverflow.com/a/7304184 semicolon_is_space() : std::ctype(get_table()){}; static mask const *get_table() { static mask rc[table_size]; rc[(int)';'] = std::ctype_base::space; rc[(int)'\n'] = std::ctype_base::space; return &rc[0]; }; }; class input { public: std::string s; unsigned long k; friend std::istream &operator>>(std::istream &in, input &i); friend std::ostream &operator<<(std::ostream &out, const input &i); }; std::istream &operator>>(std::istream &in, input &i) { in >> i.s >> i.k; return in; } std::ostream &operator<<(std::ostream &out, const input &i) { out << "String:\n" << i.s << "\nK: " << i.k << std::endl; return out; } class result { public: unsigned long longest_start = 0; unsigned long longest_end = 0; bool match_found = false; bool operator==(const result r); friend std::istream &operator>>(std::istream &in, result &r); friend std::ostream &operator<<(std::ostream &out, const result &r); }; bool result::operator==(const result r) { return longest_start == r.longest_start && longest_end == r.longest_end && match_found == r.match_found; } std::istream &operator>>(std::istream &in, result &r) { in >> r.longest_start >> r.longest_end >> r.match_found; return in; } std::ostream &operator<<(std::ostream &out, const result &r) { out << "longest_start: " << r.longest_start << " longest_end: " << r.longest_end << " match_found: "; if (r.match_found) out << "true"; else out << "false"; out << std::endl; return out; } class test_case { public: input i; result r; friend std::istream &operator>>(std::istream &in, test_case &t); friend std::ostream &operator<<(std::ostream &out, const test_case &t); }; std::istream &operator>>(std::istream &in, test_case &t) { in >> t.i >> t.r; return in; } std::ostream &operator<<(std::ostream &out, const test_case &t) { out << t.i << t.r; return out; } result find(const std::string &s, const unsigned long k) { result r; std::unordered_map char_count; unsigned long start = 0, end = 0; for (; end < s.length(); end++) { char_count[s[end]] += 1; while (char_count.size() > k) { char_count[s[start]]--; if (char_count[s[start]] == 0) { char_count.erase(s[start]); } start++; } if (char_count.size() == k && end - start > r.longest_end - r.longest_start) { r.longest_start = start, r.longest_end = end; r.match_found = true; } } if (r.match_found) { std::cout << "Longest substring is \"" << s.substr(r.longest_start, r.longest_end - r.longest_start + 1) << "\" with length " << r.longest_end - r.longest_start + 1 << ".\n"; } else { std::cout << "Could not find any match, not enough unique characters.\n"; } return r; } int main(int argc, char *argv[]) { semicolon_is_space delimeter; for (int i = 1; i < argc; i++) { std::ifstream f(argv[i]); f.imbue(std::locale(f.getloc(), new semicolon_is_space)); while (f.good()) { test_case t; f >> t; if (t.i.s.length()) { // skip empty line/string inputs std::cout << t; if (find(t.i.s, t.i.k) == t.r) { std::cout << "Test case with string \"" << t.i.s << "\" and k=" << t.i.k << " passed.\n"; } else { std::cout << std::flush; std::cerr << "TEST CASE WITH STRING \"" << t.i.s << "\" AND k=" << t.i.k << " FAILED.\n"; } std::cout << "\n"; } } } return 0; }