Ronaldo, Arnoldo and Ornaldo

Sometimes,two or more people may have names that are anagrams of each other. Let us call a group of distinct names that are all anagrams of each other an anagramic tuple .

For example (Norma, Ramon, Roman) is an example of an anagramic tuple of size 3 3 .

In names.txt , a 46K text file containing over five-thousand first names, let the size of the largest anagramic tuple be N N . Let M M be the number of anagramic tuples of size N N . What is the value of the product N × M N \times M ?

Details and assumption

  • The arrangement of the names in the tuple doesn't matter. I.e. (Tia, Tai) is the same tuple as the tuple (Tai, Tia).

  • All the names in an anagramic tuple are distinct.


The answer is 30.

This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try refreshing the page, (b) enabling javascript if it is disabled on your browser and, finally, (c) loading the non-javascript version of this page . We're sorry about the hassle.

10 solutions

Brian Rodriguez
Jul 22, 2014

Solution:

  1. Sort each word individually, place into list
  2. Sort list
  3. Linearly walk the list to find N and M.

C++ Implementation:

#include < vector >
#include < fstream >
#include < iostream >
#include < string >

using namespace std;

int main(int argc, char **argv)
{
    // Fetch the names (stored into one big string)
    ifstream input("names.txt");
    string names;
    getline(input, names);

    // Convert the long string into a vector of strings
    // Logic uses " to denote the beginning/end of a word,
    // and skips ,'s entirely.
    vector < string > words;
    bool in_word = false;
    string word;
    for (string::iterator it = names.begin(); it != names.end(); ++it) {
        switch (*it) {
            case ',':
                // Commas are useless
                continue;

            case '"':
                if (in_word) {
                    // Sort the word and insert it into the vector
                    sort(word.begin(), word.end());
                    words.push_back(word);
                    // Clear the current word to parse the begin
                    // parsing the next
                    word.clear();
                }

                in_word = !in_word;
                break;

            default:
                // All other characters are part of a word :)
                word += *it;
                break;
        }
    }

    // Sort the entire vector
    sort(words.begin(), words.end());

    // Now we can linearly search for matching anagrams.
    // Everytime the current word matches the last, we
    // have a consecutive hit. Hopefully the logic is
    // self-explainatory.
    int cur_count, max_count, consecutive_max;
    cur_count = 1;
    max_count = 1;
    consecutive_max = 1;
    word = words[0];
    for (vector < string > ::iterator
        it = words.begin() + 1;
        it != words.end();
        ++it) {
        if (word == *it) {
            cur_count++;
        } else {
            if (cur_count > max_count) {
                max_count = cur_count;
                consecutive_max = 1;
            } else if (cur_count == max_count) {
                consecutive_max++;
            }

            word = *it;
            cur_count = 1;
        }
    }

    // Done! :)
    cout << max_count * consecutive_max << endl;

    return 0;
}
Thaddeus Abiy
Jul 10, 2014

My solution relied on python's dictionary object,which is essentially a Hash Table. I am interested in other approaches to this problem,feel free to post. Mine goes as follows

All anagrams of a word have the same letters just with different arrangements so using a sorted list of the letters as a key and all its anagrams as a value we can easily solve the problem.

For example the key ('A', 'A', 'D', 'I', 'N') is associated with the array of anagrams ['DIANA', 'NADIA', 'ADINA', 'DANIA', 'DAINA', 'NAIDA'] .

After associating each key with the array of anagrams,all that is left is to find the largest values and count how many times they occur.

code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
with open('names.txt','rb') as D:
    exec('Names=[%s]'%D.read())

Dict = {}
Ms = [0]*len(Names)
for Name in Names:
    key = tuple(sorted(Name))
    try:
        Dict[key].append(Name)
    except KeyError:
        Dict[key]=[Name]
N = 0
for name in Dict:
    Count = len(Dict[name])
    Ms[Count] = Ms[Count] + 1
    if Count > N:
        N = Count

print N * Ms[N]

The largest anagramic tuples found are those of size 6 6 .

1
2
3
4
5
['DIANA', 'NADIA', 'ADINA', 'DANIA', 'DAINA', 'NAIDA']
['EDNA', 'DENA', 'DEAN', 'NEDA', 'ENDA', 'DANE']
['SHEILA', 'SHELIA', 'ELISHA', 'SHIELA', 'ASHLIE', 'LEISHA']
['ALINE', 'LIANE', 'NELIA', 'ELINA', 'LANIE', 'LAINE']
['ELISA', 'LEISA', 'ISELA', 'LESIA', 'ALISE', 'ELIAS']

There are 5 5 of them so the answer is 6 × 5 = 30 6 \times 5 = 30 .

I was about to ask why "PEEK CLAM"... :)

Calvin Lin Staff - 6 years, 11 months ago

Log in to reply

Probably because they are anagrams of keep clam :p

Varshith Reddy - 6 years, 11 months ago

Good solution.

Anuj Shikarkhane - 6 years, 11 months ago

Great Solution!,Did it in a similar way

Arkin Dharawat - 5 years, 7 months ago
Varshith Reddy
Jul 11, 2014

Here goes:

from urllib2 import urlopen
from collections import Counter
k=urlopen("https://projecteuler.net/project/names.txt").read().split(',')
l=[]
for i in k:
    l+=[''.join(sorted(i))]
dic=Counter(l)
N=max(dic.values())
M=Counter(dic.values())[N]
print M*N
Russel Simmons
Jul 11, 2014

In Python:

names = [n.strip('"') for n in open('names.txt').read().strip().split(',')]

anatuples = {} # maps sorted string to list of names that sort to that

# add each name to its appropriate tuple
for n in names:
    sn = ''.join(sorted(n))
    anatuples.setdefault(sn, []).append(n)

# count size of each tuple, computing m and n as we go in one pass
m = None
n = None
for tup in anatuples.itervalues():
    count = len(tup)
    if n is None or count > n:
        n = count
        m = 1
    elif count == n:
        m += 1

print m*n

Can you simplify this without writing program?

payal bharane - 6 years, 11 months ago

Log in to reply

Sorry, I don't understand what you mean.

Russel Simmons - 6 years, 11 months ago

Log in to reply

Payal's requesting that you write some pseudocode to describe your program.

Ryan Tamburrino - 5 years, 10 months ago
Bill Bell
May 20, 2015

Should the answer be 36?

Arulx Z
May 14, 2015

Here is what I did -

I first arranged all the alphabets in the given names in ascending order. This separated all anagramic tuples as all anagramic tuples now have the same value (because they all contain the same letters).

Then I counted and deleted the anagramic tuples and stored the size and numbers of them in variables. Then I multiplied the variables.

Here is my code -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private static List<String> list;
private static int _size = 0, _num = 0;
public static void main(String args[]) {
    String n[] = {THE ARRAY IN THE LINK};
    list = sort(n);
    for(int i = list.size() - 1; i >= 0; i--){
        int total = count(list.get(i));
        if(total > _size){
            _size = total;
            _num = 1;
        }else if(total == _size)
            _num++;
        i = list.size() - 1;
    }
    System.out.println(_size + " " + _num);
    System.out.println(_size * _num);
}

private static int count(String n){
    int c = 0;
    while(list.remove(n))
        c++;
    return c;
}   

private static ArrayList<String> sort(String n[]){
    char x[];
    for(int i = 0; i < n.length; i++){
        x = n[i].toCharArray();
        Arrays.sort(x);
        n[i] = new String(x);
    }
    return new ArrayList<String>(Arrays.asList(n));
}

It took about 0.1 sec to run

Vladimir Lem
Mar 6, 2015

I solved this problem by using C++ STL map which is useful for making array with string indices.

Since anagrams will have same composition of letters, we can use this fact to group anagrams by sorting the letters on the name.

Example: KAMI will be the same like MIKA if we sort their letters (AIKM), then they are both an anagramic tuple .

I edited the input file to fit for my code (because I don't want to make the code more complex by parsing the " and commas) by deleting the " and changing , symbol to a newline.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
#define max(x,y) ((x) > (y) ? (x) : (y))
using namespace std;

map <string,int> dat;
map <string,int>::iterator it;
string inp;
long long M,N,t;

int main()
{
    N=M=0;
    while (cin>>inp) {
        sort(inp.begin(),inp.end());
        t=++dat[inp];
        N=max(t,N);
    }
    for (it=dat.begin();it!=dat.end();it++) // iterate through the map
        if (it->second == N) M++;
    cout << N*M << endl;
    return 0;
}

Output: 30

My solution in action: ideone.com/MBFE6D

Ninad Sheth
Aug 4, 2014

This is solution in VB (Note it is not perfect one , it has alot of extra code which i was using for test purpose , if u want then you may remove it ) along with finding multiplication it also sort out all anagrams and
add it to list box , along with count , after all calculation is done , it will show multiplication at end of listbox , if u want you may remove extra code to get final compact result. link :- google drive contains exe + source code as always , if u want to use code for personal use or create tutorial then give code credits to me :D

Adrian Cleave
Jul 23, 2014

JS Version:

function sortStr(str){
    var az = str.split('').sort().join('');
    return az;
}

function main(){
    var r = {};
    var counts = [];

    for(var i=0;i<names.length;i++){
        names[i] = sortStr(names[i]);
        r[names[i]] = 0;
    }

    names.sort();

    for(var c=0;c<names.length;c++){
        if(names[c] == names[c+1]){
            r[names[c]]++;
        }
    }
    for(var o in r){
        if( !counts[r[o]] ) counts[ r[o] ] = [];
        counts[ r[o] ].push(o);      
    }
    console.log(counts);
}
main();

My solution is in C# and too large to write here however go to the bottom of the solution to see it. I prefer readability of compactness. The general approach is to firstly write something that will read the given file and parse it so that each name is in an array. We have an array of strings which we will call names.

Next we create an array of arrays. The represents an array of tuples. Now go through names and select the first one. Store the first name into an array called tuple and delete the name from names. We now loop through names and look for names that have the same length AND are anagrams of each other. This means we will need some code to determine if two names are anagrams.

Once we find a name we will add it to tuple and delete it from the list. Remember that by deleting from the array the index of the array has shifted. Therefore decrementing the indexer before finishing the loop is a good idea.

When we have finished going through all the names with respect to our first pick we now add tuple to the array of arrays. We then pick the first element in names again and repeat the process of finding anagrams. Note that we always pick the first element as it will be deleted and shifted along anyway.

When we are finished with this process we should have an array of arrays that we can search. First we search for the array inside our array with the largest count. We denote this maxCount or N N . We then search for arrays in our array that have length maxCount and denote the number M M . Thus the solution to the problem is M × N M\times N .

If you have computed this correctly we should have M × N = 5 × 6 = 30 M\times N=5\times6=\boxed{30} .

Here is my code on pastebin if anybody is interested.

0 pending reports

×

Problem Loading...

Note Loading...

Set Loading...