8
\$\begingroup\$

Recently there was a question on Stack Overflow where the OP was trying to write a function to find the word in a string that has the most repeated letters. It is of course not hard to write one up in seconds, and I wrote one in Javascript as short as I can for fun. But I'm not an expert in code golfing, so I wonder how much shorter can this simple program be!


Challenge

Write a program or function that takes in a string of words and return or print the word with the most repeating single letters.

Rules:

  • Choose the word with the greatest number of repeating single letters (see examples below)

  • If no word has repeating letters, return -1.

  • If two words have the same maximum number of repeating letters, choose the one closer to the start of the string.

  • The shortest submission in bytes wins.

Input

Take as input a string consisting of one or more space-delimited words. Input can be from STDIN (or closest alternative), command line parameters, or function arguments.

Output

Print the output to STDOUT to return it.

Examples

Consider the string aaabbb cccc. This contains two words: aaabbb and cccc. The word aaabbb has 3 a's and 3 b's, and cccc has 4 c's. So the maximum number of repeated letters in aaabbb is 3 and the maximum in cccc is 4. We want to choose the word with the maximum number of repeated single letters, so the output for aaabbb cccc should be cccc.

Other test cases:

Today, is the greatest day ever!  --> greatest
This is a great day               --> -1
aaabbb cccc                       --> cccc
\$\endgroup\$
  • \$\begingroup\$ What if more than one words have the same # of repeated letters? Do we print any or all? \$\endgroup\$ – Maltysen Jul 20 '15 at 6:54
  • \$\begingroup\$ @Maltysen Look at the first example - see ever \$\endgroup\$ – isaacg Jul 20 '15 at 6:57
  • \$\begingroup\$ @isaacg but greatest has two repeating, t and e. \$\endgroup\$ – Maltysen Jul 20 '15 at 6:58
  • 2
    \$\begingroup\$ I'm not sure what is meant by the number of repeated letters. I assume aabb has 2 repeated letters. Would aaaabb be considered to have 4 repeated letters (2nd, 3rd, 4th a, 2nd b) or 2 repeated letters (a and b). \$\endgroup\$ – feersum Jul 20 '15 at 6:59
  • 1
    \$\begingroup\$ Note that the original question is from Coderbyte. I looked on their website for copyright information (since this is a reproduction of Letter Count I) but I couldn't find anything. \$\endgroup\$ – Alex A. Jul 21 '15 at 17:54

16 Answers 16

7
\$\begingroup\$

C - GCC - 159 145 135 Bytes

x=0,w=0,i,j,c;main(int _,char**z){while(--_)for(j=i=0;_[z][i];j=++i,x=c>x?w=_,c:x)for(c=0;j--;)c+=z[_][i]==z[_][j];puts(x?z[w]:"-1");}

Will update when I can truncate it a bit

How to compile: gcc -w cg.c

How to run: ./a.out word1 word2 aass ddaaa ssdddd

Output: ssdddd

No-Output/No-match: -1

First off, I cheated a bit, by taking in the words as program arguments, but getting GCC to parse the words and give me a word count for free was too much reward to pass up.

How does it work?

We have 3 nested loops. The first incrementing through each word, the next two are mimicking a bubble sort to compare values. The first letter is never compared with itself, and each subsequent letter is compared to each previous letter. Whenever a word has more same letters than a previous word, the word is stored in x, and the count of how many same letters is also stored.

GCC Abuse

We use implicit global auto int delcarations for our integers. We allow argv to be an int instead of a char (currently a char, this is a TODO). We make use of default functions such as puts and getchar. We also make use of the comma operator to overload our trinary (conditional) operator.

Want 2 less bytes?

Replace "-1" with *z and name the file -1

Program unobfuscated and untested:

int main(int argc, char * argv[])
{
    int word = 0           // Current most repeat letters word
    int count = 0;        // Current most repeat letters
    int i, j, increment; // Counter variables

    while(--argc != 0) // While we have words from program parameters
    {
        for(j = i = 0; argv[argc][i] != '\0'; j = ++i) // Iterate through each letter until end of word
        {
            for(increment = 0; j > 0; j--) // Iterative backwards through each letter
            {
                if(argv[argc][i] == argv[argc][j])
                {
                    increment++;
                }
            }
            if(increment > count) // New greatest lettered word
            {
                word = argc;
                count = increment;
            }
        }
    }

    if(word == 0)
    {
        puts("-1");
    }
    else
    {
        puts(argv[word]);
    }

    return 0;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to PPCG! Nice golfing; if you get time, I'd love to see an explanation of how your code works. \$\endgroup\$ – Toby Speight Jul 21 '15 at 13:52
  • \$\begingroup\$ Updated OP with an explanation \$\endgroup\$ – Jake Jul 21 '15 at 14:12
  • \$\begingroup\$ No GCC abuse, just some old style but regular C \$\endgroup\$ – edc65 Jul 22 '15 at 7:18
  • \$\begingroup\$ thats a record! congrats \$\endgroup\$ – Abr001am Jul 25 '15 at 8:41
4
\$\begingroup\$

Pyth, 14 bytes

eoeS/LNN+czd_1

Demonstration. Test harness.

eoeS/LNN+czd_1
                  Implicit: z = input(), d = ' '
         czd      chop z on delimeter d.
        +   _1    Add an -1 to the end of the list.
 o                Order the list by
  eS              the maximum of
    /LN           the occurence count in the word
       N          of letters in the word.
                  (If we are mapping over -1, this will give -1/-1 = 1)
                  Since Python's sort is stable, this will leave -1 at the end if
                  all words have maximum repetition count 1.
e                 Take the final element of the resulting list and print it.
\$\endgroup\$
  • \$\begingroup\$ Still doesn't seem to be working. As per feersum's comment "aaaabb" should have 4 repeats and thus more than "greatest". But pyth.herokuapp.com/… still gives greatest. \$\endgroup\$ – Maltysen Jul 20 '15 at 7:45
  • \$\begingroup\$ Putting an l before .- seems to do it. \$\endgroup\$ – Maltysen Jul 20 '15 at 7:46
  • \$\begingroup\$ @Maltysen Right, I was silly and attempted to use string sorting. \$\endgroup\$ – isaacg Jul 20 '15 at 7:46
  • \$\begingroup\$ This is short and beautiful. \$\endgroup\$ – Derek 朕會功夫 Jul 23 '15 at 2:19
  • \$\begingroup\$ @Derek朕會功夫 Thanks! Consider upvoting. \$\endgroup\$ – isaacg Jul 23 '15 at 2:33
2
\$\begingroup\$

K, 35 bytes (missing -1 requirement, just noticed, but time to sleep)

{1#w[>{|/{#:&:x=y}[x]'x}'w:" "\:x]}

how it works:

1 take

1#

of the words indexed by

w[

the indices to put in ascending order

>

the maximum

|/

count where

#:&:

string = char

x=y

for each char in word

'x

for each word where w (words) are

'w:

the string split by spaces

\:x

Obviously I sacrificed a bit of precision for expressiveness and legibility in the English explanation. I hope it was interesting.

\$\endgroup\$
  • 1
    \$\begingroup\$ I think you can follow the requirement by adding ,-1 to the end of the function. \$\endgroup\$ – kirbyfan64sos Jul 20 '15 at 17:21
  • \$\begingroup\$ You should be able to leave off the colons for #:&: since from context they should parse as their monadic forms. You could also use @ to index instead of brackets, and first (*) instead of 1#. \$\endgroup\$ – JohnE Jul 21 '15 at 19:12
2
\$\begingroup\$

Haskell, 100 bytes

import Data.List
f=g.last.sort.map((,)=<<last.sort.map length.group.sort).words
g(1,_)="-1"
g(_,w)=w

Usage example: f "Today, is the greatest day ever!" -> "greatest"

How it works:

                                                words  -- split input at spaces into list of words
          map(                                 )       -- for each word
              (,)=<<                                   -- build a pair, where the second element is the word itself
                                                       -- and the first element is made by
                                           sort        -- sort the letters
                                      group            -- group equal letters
                            map length                 -- take length of each group
                        sort                           -- sort the lengths
                    last                               -- take the last
                                                       -- now we have a list of (l,w) pairs where l is how often the most frequent letter occurs for word w
     sort                                              -- sort the list
 last                                                  -- take last element
g                                                      -- call g which checks the "-1" case 

Haskell, 79 77 bytes (untested)

import Data.List
last.sortOn(last.sort.map length.group.sort).words.(++" -1")

This uses sortOn from Data.List v4.8.0.0, which I don't have installed, so I cannot test it.

\$\endgroup\$
2
\$\begingroup\$

CJam, 25 bytes

lS/{_$e`$W=0=(\}%2/$W=~W?

Try it online

Explanation:

lS/   Get input and split at spaces.
{     Start of loop over all words.
  _     Copy, need to keep original word.
  $     Sort letters.
  e`    RLE.
  $     Sort RLE result. Sort is increasing by count.
  W=    Get last count/letter pair, which corresponds to largest count.
  0=    Extract count from pair.
  (     Decrement count, so that it has a falsy value when the count is 1.
  \     Swap count and word, so that we can sort the pairs by count.
}%    End of loop over words.
2/    Split list into pairs, which are the count/word pairs.
$     Sort the pairs.
W=    Get last one, which is the largest count.
~     Unwrap the pair.
W?    Output word if count is truthy, -1 otherwise.
\$\endgroup\$
2
\$\begingroup\$

Python 2, 97 77 Bytes

Fairly straightforward solution, just maps the input (surrounded by quotes) to a tuple containing the word and the number of repeated characters. Gets the maximum, and prints out the word if the most repeated letter is repeated at all, otherwise it prints -1.

I saved 20(!) bytes by rearranging the order of input so that I didn't need a key for finding the max.

j=max((max(map(x.count,x)),x)for x in input().split())
print[-1,j[1]][j[0]>1]
\$\endgroup\$
1
\$\begingroup\$

SWI-Prolog, 158 154 149 bytes

a(A,R):-split_string(A," ","",B),findall(X:Z,(member(Z,B),string_codes(Z,D),length(D,L),sort(D,E),length(E,M),X is M-L,X<0),S),sort(S,[_:R|_]);R= -1.

Example: a("Today, is the greatest day ever!",R). outputs R = "greatest" ..

\$\endgroup\$
1
\$\begingroup\$

JavaScript, 86 111 108 bytes

s=>(l=(x=s.split` `,r=x.map(l=>(/(.)\1+/.exec(l)||[''])[0].length),x)[r.indexOf(k=Math.max(...r))],k<2?-1:l)

Definitely golf-able, the whole -1 thing added about 20 bytes.

\$\endgroup\$
1
\$\begingroup\$

R, 107 bytes

w=scan(,"");l=sapply(w,function(x)max(table(strsplit(x,"")[[1]])));cat(ifelse(max(l)>1,w[which.max(l)],-1))

This reads from STDIN and prints to STDOUT.

Ungolfed + explanation:

# Read a string and split it into a vector on spaces
w <- scan(, "")

# Get the maximum number of letter repeats in each element of w
l <- sapply(w, function(x) max(table(strsplit(x, "")[[1]])))

# If the largest number in l is 1, print -1, otherwise get the word
cat(ifelse(max(l) > 1, w[which.max(l)], -1)
\$\endgroup\$
1
\$\begingroup\$

C# 166 Bytes

string a(string i){var z=i.Split(' ');int g=1,c=0;var m="-1";foreach(var w in z)foreach(var l in w.Distinct()){c=w.Where(x=>x==l).Count();if(c>g){g=c;m=w;}}return m;}

Straightforward coding. Nothing Special in here.

C# sucks for code golfing :-/

\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES7?), 99

Using array comprehension, that is implemented in Firefox but no more included in EcmaScript 6.

Test using the snippet below (Firefox only)

f=s=>s.split(' ').map(w=>[for(c of(l=[m=0],w))(n=l[c]=-~l[c])>m?m=n:0]&&m>x&&(x=m,v=w),x=1,v=-1)&&v

// TEST
out=x=>O.innerHTML+=x+'\n';

test=x=>out(x+' -> '+f(x))

;["aaabbb cccc","This is a great day","Today, is the greatest  day ever a!"]
.forEach(t=>test(t));
<pre id=O></pre>
Try:<input id=I><button onclick='test(I.value),I.value=""'>-></button>

Ungolfed And more compatible

function f(s)
{
  v = -1;
  x = 1;
  s.split(' ')
  .forEach(function(w){
    l=[];
    m=0;
    w.split('').forEach(function(c){
      n=l[c]=-~l[c];
      if (n>m) m=n;
    })
    if (m>x) x=m,v=w;
  })
  return v;
}

// TEST
out=function(x) { O.innerHTML+=x+'\n' }

test=function(x) { out(x+' -> '+f(x)) }

;["aaabbb cccc","This is a great day","Today, is the greatest  day ever a!"]
.forEach(function(t) { test(t)} );
<pre id=O></pre>
Try:<input id=I><button onclick='test(I.value),I.value=""'>-></button>

\$\endgroup\$
1
\$\begingroup\$

Python, 58

max('-1',*input().split(),key=lambda w:len(w)-len(set(w)))
\$\endgroup\$
1
\$\begingroup\$

C (167)

double m,k,n=k=2,*O,V[256];char*f(char*c,char*s){return*c?((*O=!((n+=!(*c*=*c!=32))>1.1/modf(*(O=V+*c),&m))*m+1/n+1)>k)?f(c+!!(k=*O),c):f(c+1,s):s?!*s?s+1:f(c,s-1):0;}

TRY IT

HOW DOES THIS WORK?

  • the function is inner recursive one inside another recursive function, the insider function retrieves the beginning of a string where an inclusive character is returned by first function.
  • maximum number is given by hashing characters over an ascii table.
\$\endgroup\$
1
\$\begingroup\$

Q (44 bytes)

{w l?0|/l:{max(#:)'[(=)x]}'[w:" "vs"-1 ",x]}

ungolfed

{
    words:" " vs "-1 ",x;
    counts:{max count each group x} each words;
    : words counts ? max counts;
}
\$\endgroup\$
1
\$\begingroup\$

Haskell 96 Bytes


r o=(last.sort.map length$(group.sort)o,o)
w p=n$maximum(map(r)(words p))
n (1,_)="-1"
n (_,w)=w

```


r is a function that takes a word and return a tuple (n,w) where n is the number of occurrence of the most occurring char in the word w. For instance let x="norep", y="dnredundant", then r x=(1,norep), r y=(3,ndredundant)

w is a function that takes a string containing a number of space-separated words and:

  1. Split the list on space words p

  2. foreach word create a list of (n,w)

  3. take the tuple that has the greatest n (occurrence counter)

  4. If it has n equals to 1 simply return the string -1, the word itself (stored in the second component of the tuple) otherwise.

For instance take p="Today, is the greatest day ever!"

  1. produces ["Today,","is","the","greatest","day","ever!"]

  2. [(1,"Today,"),(1,"is"),(1,"the"),(2,"greatest"),(1,"day"),(2,"ever!")]

  3. (2,"greatest")

  4. 2 != 1 then greatest is the solution!

\$\endgroup\$
1
\$\begingroup\$

Pure Bash (no external commands) 129 bytes

This is quite long, but still compares favourably with some of the other longer entries.

m=1
w=-1
for b;{
declare -A a
for((i=0;i<${#b};i++));{
c=${b:$i:1}
let a[$c]++
d=${a[$c]}
((d>m))&&w=$b&&m=$d
}
unset a
}
echo $w

I'm not entirely happy with some of the constucts, having to use that inner for loop is annoying. Any suggestions?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.