my first post on here, be gentle ;)
Find all anagrams within a text
code-golf
Somehow I stumbled upon an implementation of a school assignment from about a year ago, and after having seen many amazing and mindblowing code-golf solutions on here, I thought it's time I bring my own challenge and see how much you guys can blow my mind again ;)
The assignment
Given a text, find all words that have at least one other word in the text as an anagram (case insensitive). Multiple occurrences of the same word are not counted.
The output shall be grouped by words that are an anagram of each other.
Rules
- How you handle input/output is up to you. Function-parameters, file-io, standard in/out, whatever works the best for you.
- The input-text may be empty, it may also be very big. You are required to be able to handle whatever memory your language of choice can handle.
- The output does not have any fixed formatting. That means you may put them each group at a line, or put them all at one line but use different delimiters, a 2d array, some other exotic data-structure your language of choice happens to have, as long as it makes reasonable sense, it is considered correct. (This means that for example if you are just writing a function, that function does not need to display the output, it could just provide it as a return-value.) Just keep in mind the requirement that the words that are an anagram of each other should be grouped together.
- The order in which the output appears does not matter. That applies to the order of the groups as well as the order of the words within the group.
- A group of only one word is invalid, since that fails the "have at least one other word in the text as an anagram" requirement. (just omit them from your output ^^)
- Each word should only appear once in the output
Example
Given the following input text (the actual text I was given as example by school :P, no idea where this text is coming from...)
Parts of the world have sunlight for close to 24 hours during summer. Dan went to the north pole to lead an expedition during summer. He had a strap on his head to identify himself as the leader. Dan had to deal with the sun never going down for 42 consecutive days and his leadership strap soon became a blindfold. He wondered what kind of traps lay ahead of him.
the following output would be correct:
- 24, 42
- deal, lead
- and, dan
- parts, strap, traps
Or this would also be correct:
24, 42 | deal, lead | and, dan | parts, strap, traps
This one would not
24, 42 , deal, lead , and, dan , parts, strap, traps
(since the groups are not obvious)
My own (non golfed) version to check
The is the exact program I submitted to school back then. You may use it to check your own results.
Added bonus: If it happens to be that this program has a bug (I haven't found them yet) your submission is allowed to have it as well, since it is used to check the result. (In that case you are of course not required to have said bugs)
#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include <set>
#include <algorithm>
#include <cstring>
void stringRemoveInterpunction(std::string& string);
void stringToLower(std::string& string);
std::string stringToAnagramIdentifier(std::string word);
/**
* due to use of std::ispunct and std::tolower it may not work for text with non-ascii characters??!
*/
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "usage: " << argv[0] << " <filename>" << std::endl;
return 1;
}
std::ifstream fileStream(argv[1]);
if (!fileStream) {
std::cerr << "Could not open file " << argv[1] << std::endl;
return 2;
}
// map to store the anagrams, key is so called "anagram identifier", value is a list of the words.
std::map<std::string, std::set<std::string>> anagrams;
// read words separated by whitespace from the file
for (std::string word; fileStream >> word;) {
// remove interpunction & convert to lowercase, since casing should be ignored
stringRemoveInterpunction(word); stringToLower(word);
// add to anagrams-store
anagrams[stringToAnagramIdentifier(word)].insert(word);
}
// display all the anagrams
for (auto anagram : anagrams) {
// skip entries which contains only one item, no anagrams found
if (anagram.second.size() <= 1) {
continue;
}
// output a comma-separated list of the anagrams
auto anagramIterator = anagram.second.begin();
std::cout << *anagramIterator++;
while (anagramIterator != anagram.second.end()) {
std::cout << ", " << *anagramIterator++;
}
std::cout << std::endl;
}
return 0;
}
void stringRemoveInterpunction(std::string& string) {
string.erase(std::remove_if(string.begin(), string.end(), std::ptr_fun<int, int>(&ispunct)), string.end());
}
void stringToLower(std::string& string) {
std::transform(string.begin(), string.end(), string.begin(), std::ptr_fun<int, int>(&std::tolower));
}
std::string stringToAnagramIdentifier(std::string word) {
// sort the characters
std::sort(word.begin(), word.end());
return word;
}
Sandbox Questions
- Do i need to add other tags, or is just code-golf enough?
- I'm not completely sure about the upper-limit of the input text. My idea was that the code should be able to handle any size input as long as its within the memory-limits of the language. Like you don't have to write "memory optimal code" or something, but also shouldn't asume it is smaller than X. I could also just pick an upper limit of "1 kilobyte" or something to avoid any uncertainty about the requirements
- Any other parts that are not clear?