|
| 1 | +#include <iostream> |
| 2 | +#include <unordered_map> |
| 3 | + |
| 4 | +// map of all upper case and lower case chars with prime numbers |
| 5 | +const static std::unordered_map<char, int> hash{ |
| 6 | + {'a' , 2}, |
| 7 | + {'b' , 3}, |
| 8 | + {'c' , 5}, |
| 9 | + {'d' , 7}, |
| 10 | + {'e' , 11}, |
| 11 | + {'f' , 13}, |
| 12 | + {'g' , 17}, |
| 13 | + {'h' , 19}, |
| 14 | + {'i' , 23}, |
| 15 | + {'j' , 29}, |
| 16 | + {'k' , 31}, |
| 17 | + {'l' , 37}, |
| 18 | + {'m' , 41}, |
| 19 | + {'n' , 43}, |
| 20 | + {'o' , 47}, |
| 21 | + {'p' , 53}, |
| 22 | + {'q' , 59}, |
| 23 | + {'r' , 61}, |
| 24 | + {'s' , 67}, |
| 25 | + {'t' , 71}, |
| 26 | + {'u' , 73}, |
| 27 | + {'v' , 79}, |
| 28 | + {'w' , 83}, |
| 29 | + {'x' , 89}, |
| 30 | + {'y' , 97}, |
| 31 | + {'z' , 101}, |
| 32 | + {'A' , 103}, |
| 33 | + {'B' , 107}, |
| 34 | + {'C' , 109}, |
| 35 | + {'D' , 113}, |
| 36 | + {'E' , 127}, |
| 37 | + {'F' , 131}, |
| 38 | + {'G' , 137}, |
| 39 | + {'H' , 139}, |
| 40 | + {'I' , 149}, |
| 41 | + {'J' , 151}, |
| 42 | + {'K' , 163}, |
| 43 | + {'L' , 167}, |
| 44 | + {'M' , 173}, |
| 45 | + {'N' , 179}, |
| 46 | + {'O' , 181}, |
| 47 | + {'P' , 191}, |
| 48 | + {'Q' , 193}, |
| 49 | + {'R' , 197}, |
| 50 | + {'S' , 199}, |
| 51 | + {'T' , 211}, |
| 52 | + {'U' , 223}, |
| 53 | + {'V' , 227}, |
| 54 | + {'W' , 229}, |
| 55 | + {'X' , 233}, |
| 56 | + {'Y' , 239}, |
| 57 | + {'Z' , 241 } |
| 58 | +}; |
| 59 | + |
| 60 | +// returns the hash of string which is unique for the combination |
| 61 | +// of characters in that string. |
| 62 | +// By unique factorization theorem there won't be any hash collisions |
| 63 | +long long get_hash(const std::string& str) { |
| 64 | + long long hash_val = 1; |
| 65 | + for (char c : str) { |
| 66 | + hash_val *= hash.at(c); |
| 67 | + } |
| 68 | + |
| 69 | + return hash_val; |
| 70 | +} |
| 71 | + |
| 72 | +// This funtion calculates the hash of string |
| 73 | +long long anagram_count(const std::string& parent, const std::string& child) { |
| 74 | + long long hash_child = get_hash(child); |
| 75 | + |
| 76 | + // to keep count of all anagrams |
| 77 | + long long count = 0; |
| 78 | + |
| 79 | + // get the hash of first n characters where n is length of child |
| 80 | + long long hash_val = get_hash(parent.substr(0, child.size())); |
| 81 | + if (hash_val == hash_child) { |
| 82 | + count++; |
| 83 | + } |
| 84 | + |
| 85 | + // As we are moving ahead one char at a time, to calculate the hash_val of next string |
| 86 | + // we can divide the last hash_val by the hash of char which is moving out |
| 87 | + // and multiply by the hash of char being moving in |
| 88 | + // e.g. in abcd, if hash_val of "abc" is n, then hash_val of "bcd" is: n / hash.at('a') * hash.at('d'); |
| 89 | + for (size_t index = child.size(); index < parent.size(); ++index) { |
| 90 | + hash_val /= hash.at(parent[index - child.size()]); |
| 91 | + hash_val *= hash.at(parent[index]); |
| 92 | + if (hash_val == hash_child) { |
| 93 | + count++; |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + return count; |
| 98 | +} |
| 99 | + |
| 100 | +int main() { |
| 101 | + std::cout << anagram_count("forbtorfxdofrm", "for") << std::endl; // 3 |
| 102 | + std::cout << anagram_count("aabaabaa", "aaba") << std::endl; // 4 |
| 103 | + std::cout << anagram_count("AdnBndAndBdaBn", "dAn") << std::endl; // 4 |
| 104 | + std::cout << anagram_count("AbrAcadAbRa", "cAda") << std::endl; // 2 |
| 105 | + return 0; |
| 106 | +} |
0 commit comments