37
\$\begingroup\$

This challenge is NinjaBearMonkey's prize for winning my Block Building Bot Flocks! challenge with the Black Knight submission. Congratulations NinjaBearMonkey!

The challenge here is fairly simple, but has a variety of possible approaches. The story goes that in the world of Isometric Illusions, there are 6 different types of creatures:

  1. Ninjas, abbreviated N
  2. Bears, abbreviated B
  3. Monkeys, abbreviated M
  4. NinjaBears, abbreviated NB
  5. BearMonkeys, abbreviated BM
  6. NinjaBearMonkeys, abbreviated NBM

(NinjaBearMonkey is, of course, the last, most powerful type.)

Your task is to take a census of these creatures when they are lined up side-by-side, i.e. when their abbreviation strings are concatenated. The caveat is that you need to make sure not to over-count the parts of some creatures as separate creatures that happen to look similar. The creatures will line up such that:

  • Any instance of NBM is 1 NinjaBearMonkey and 0 other creatures.
  • Any instance of NB not followed by M is 1 NinjaBear and 0 other creatures.
  • Any instance of BM not preceded by N is 1 BearMonkey and 0 other creatures.
  • Otherwise, instances of N, B, and M are single Ninjas, Bears, and Monkeys respectively.

The line is read from left to right.

So, for example, in the line of creatures NBMMBNBNBM, there are 0 Ninjas, 1 Bear, 1 Monkey, 1 NinjaBear, 0 BearMonkeys, and 2 NinjaBearMonkeys.

Challenge

Write a program or function that takes in a string of the characters N, B, and M, and prints or returns how many of each of the 6 types of creatures are present in it.

The output should have the form

#N #B #M #NB #BM #NBM

with the respective creature count replacing each # sign. All 6 counts must be shown, separated by spaces, even when they are 0. However, they may be in any order (e.g. #NBM could come first).

Also:

  • The input string will only contain the characters N, B, and M.
  • If the empty string is input, then all the counts are 0.
  • The output may optionally contain a single leading and/or trailing space, and/or a single trailing newline.

The shortest submission in bytes wins.

Examples

Input: NB
Output: 0N 0B 0M 1NB 0BM 0NBM

Input: NBM
Output: 0N 0B 0M 0NB 0BM 1NBM

Input: NBMMBNBNBM (example from above)
Output: 0N 1B 1M 1NB 0BM 2NBM

Input: MBNNBBMNBM
Output: 1N 1B 1M 1NB 1BM 1NBM

Input: NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM
Output: 17N 6B 14M 5NB 8BM 3NBM

\$\endgroup\$
  • 53
    \$\begingroup\$ I approve this challenge. \$\endgroup\$ – NinjaBearMonkey Jun 18 '15 at 1:51
  • \$\begingroup\$ Just to confirm: if all you had was 2 NinjaBearMonkeys, you can't form a line? Because they can't stand next to each other? \$\endgroup\$ – Alan Campbell Jun 18 '15 at 7:12
  • 3
    \$\begingroup\$ @AlanCampbell No. NBMNBM would be perfectly valid input. Reading it from left to right there are clearly 2 NinjaBearMonkeys. \$\endgroup\$ – Calvin's Hobbies Jun 18 '15 at 8:50

22 Answers 22

20
\$\begingroup\$

Pyth, 22 bytes

 f|pd+/zTT=:zTd_.:"NBM

Quite hackish way to save 1 byte, thanks to @Jakube.


Pyth, 23 bytes

FN_.:"NBM")pd+/zNN=:zNd

Demonstration.

Prints in reverse order, with a trailing space and no trailing newline.

.:"NBM") is all the substrings, _ puts them in the right order, /zN counts occurences, and =:zNd in-place substitutes each occurence of the string in question with a space.

FN_.:"NBM")pd+/zNN=:zNd
FN                         for N in                            :
  _                                 reversed(                 )
   .:     )                                  substrings(     )
     "NBM"                                              "NBM"
           pd              print, with a space at the end,
              /zN          z.count(N)
             +   N                    + N
                  =:zNd    replace N by ' ' in z.
\$\endgroup\$
23
\$\begingroup\$

JavaScript ES6, 86 bytes

f=s=>'NBM BM NB M B N'.replace(/\S+/g,e=>(i=0,s=s.replace(RegExp(e,'g'),_=>++i))&&i+e)

(I just had to answer this.) It goes through each substring of NBM, starting with the longer ones, which take higher priority. It searches for each occurrence of that particular string and removes it (in this case replacing it with the current count so it won't be matched again). It finally replaces each substring with the count + the string.

This Stack Snippet is written in the ES5 equivalent of the above code to make it easier to test from any browser. It is also slightly ungolfed code. The UI updates with every keystroke.

f=function(s){
  return'NBM BM NB M B N'.replace(/\S+/g,function(e){
    i=0
    s=s.replace(RegExp(e,'g'),function(){
      return++i
    })
    return i+e
  })
}

run=function(){document.getElementById('output').innerHTML=f(document.getElementById('input').value)};document.getElementById('input').onkeyup=run;run()
<input type="text" id="input" value="NBMMBNBNBM" /><br /><samp id="output"></samp>

\$\endgroup\$
  • \$\begingroup\$ Could you change the regex part to 'NBM<newline>BM<newline>...<newline>N'.replace(/./g, ...)', where the <newline>s are literal newlines and the 's are backticks, forming an ES6 template string? Saves two bytes in the regex (. doesn't match newlines). \$\endgroup\$ – wchargin Jun 22 '15 at 4:34
  • \$\begingroup\$ @WChargin Unfortunately, no, because the output must be space-separated. \$\endgroup\$ – NinjaBearMonkey Jun 22 '15 at 17:50
17
\$\begingroup\$

Python 2, 78

n=input()
for x in"NBM BM NB M B N".split():n=`n`.split(x);print`len(n)-1`+x,

A variant of Vioz-'s answer. Fun with Python 2 string representations!

Counts occurrences of the substring indirectly by splitting on it, counting the parts, and subtracting 1. Instead of replacing the substrings by a filler symbol, replaces the string by the list that split produced. Then, when we take its string representation, the parts are separated by spaces and commas.

\$\endgroup\$
  • 5
    \$\begingroup\$ That's insane! Excellently insane, but still insane. \$\endgroup\$ – Sp3000 Jun 18 '15 at 10:43
  • \$\begingroup\$ Nice work! Didn't think of that :) \$\endgroup\$ – Kade Jun 18 '15 at 11:42
14
\$\begingroup\$

Ruby, 166 80 72 68 characters

f=->s{%w(NBM BM NB M B N).map{|t|c=0;s.gsub!(t){c+=1};c.to_s+t}*' '}

Explanation:

  • The counting is done in reverse. This is because the longer ninjas and bears and monkeys take precedence over the shorter ones.

  • For NBM, BM, and NB, the sequences are gsub!'d out of the original string with a block to count how many of these sequences exist (yes, the function modifies its argument).

    • However, they can't be replaced with nothing, since otherwise BNBMM would be counted as NBM and BM instead of B, NBM, and M (because when the NBM would be removed, it would put the B and M together and there wouldn't be a way to distinguish it). Originally I returned a single character string (.gsub!('NBM'){c+=1;?|}), but I realized I could just return the result of the += (which is a number, so it can't be any of N B M).
  • For M, B, and N, I can just count how many of them there are in the string (no need to remove them via gsub!). Now it's a loop (don't know why I didn't think of that in the first place), so these are done the same way.


Similar solution in Ostrich, 54 51 chars:

:s;`NBM BM NB M B N`" /{:t0:n;s\{;n):n}X:s;nt+}%" *

Unfortunately not a valid solution, as there is a bug in the current Ostrich version (that is now fixed, but after this challenge was posted).

\$\endgroup\$
  • \$\begingroup\$ You can save 3 chars by using the array notation %w(NBM BM NB M B N) and removing the split. \$\endgroup\$ – DickieBoy Jun 18 '15 at 9:34
  • \$\begingroup\$ @DickieBoy That's actually 4 chars; thanks! \$\endgroup\$ – Doorknob Jun 18 '15 at 9:59
  • \$\begingroup\$ Ah yes, the dot! \$\endgroup\$ – DickieBoy Jun 18 '15 at 10:00
14
\$\begingroup\$

Java, 166 162

void f(String a){String[]q="NBM-NB-BM-N-B-M".split("-");for(int i=0,c;i<6;System.out.print(c+q[i++]+" "))for(c=0;a.contains(q[i]);c++)a=a.replaceFirst(q[i],".");}

And with a few line breaks:

void f(String a){
    String[]q="NBM-NB-BM-N-B-M".split("-");
    for(int i=0,c;i<6;System.out.print(c+q[i++]+" "))
        for(c=0;a.contains(q[i]);c++)
            a=a.replaceFirst(q[i],".");
}

It works pretty simply. Just loop over the tokens, replacing them with dots and counting as long as the input contains some. Counts the big ones first, so the little ones don't mess it up.

I originally tried replacing all at once and counting the difference in length, but it took a few more characters that way :(

\$\endgroup\$
  • 2
    \$\begingroup\$ As a Java dev, I want to make this shorter and see Java win for a change. After staring at it for awhile I have yet to find a way to make it shorter. \$\endgroup\$ – DeadChex Jun 18 '15 at 18:40
  • 1
    \$\begingroup\$ Well it definitely isn't going to win overall. The current leader is 22 bytes and there's just no way to do anything meaningful in Java in that size. My println statement alone is larger than that. I'm satisfied with it, though :D \$\endgroup\$ – Geobits Jun 18 '15 at 18:57
  • 1
    \$\begingroup\$ I'm a bit late but I found a way... change String q[]= to String[]q= \$\endgroup\$ – DeadChex Jun 23 '15 at 16:14
  • 1
    \$\begingroup\$ Nice! Can't believe I missed that, it's on my standard list of things to look at :) \$\endgroup\$ – Geobits Jun 23 '15 at 17:07
  • \$\begingroup\$ I only discovered it after trying to get into Code Golf as a JavaDev, I'm quite surprised at some of the stuff you can do \$\endgroup\$ – DeadChex Jun 23 '15 at 17:14
11
\$\begingroup\$

CJam, 36 32 31 bytes

l[ZYX]"NBM"few:+{A/_,(A+S@`}fA;

Thanks to @Optimizer for golfing off 1 byte.

Try it online in the CJam interpreter.

How it works

l                                e# Read a line L from STDIN.
 [ZYX]"NBM"                      e# Push [3 2 1] and "NBM".
           few                   e# Chop "NBM" into slices of length 3 to 1.
              :+                 e# Concatenate the resulting arrays of slices.
                {          }fA   e# For each slice A:
                 A/              e#   Split L at occurrences of A.
                   _,(           e#   Push the numbers of resulting chunks minus 1.
                      A+         e#   Append A.
                        S        e#   Push a space.
                         @`      e#   Push a string representation of the split L.
                              ;  e# Discard L.
\$\endgroup\$
  • \$\begingroup\$ N* -> ` should be enough. \$\endgroup\$ – Optimizer Jun 18 '15 at 11:53
  • \$\begingroup\$ @Optimize: That works well. Thanks. \$\endgroup\$ – Dennis Jun 18 '15 at 14:18
7
\$\begingroup\$

R, 153 134 118

This got longer really quickly, but hopefully I'll be able to shave a few. Input is STDIN and output to STDOUT.

Edit Change of tack. Got rid of the split string and counting parts. Now I replace the parts with a string one shorter than the part. The difference between the string lengths is collected for output.

N=nchar;i=scan(,'');for(s in scan(,'',t='NBM BM NB M B N'))cat(paste0(N(i)-N(i<-gsub(s,strtrim('  ',N(s)-1),i)),s),'')

Explanation

N=nchar;
i=scan(,'');                     # Get input from STDIN
for(s in scan(,'',t='NBM BM NB M B N'))  # Loop through patterns
  cat(                           # output
    paste0(                      # Paste together
      N(i) -                     # length of i minus
      N(i<-gsub(                 # length of i with substitution of
        s,                       # s
        strtrim('  ',N(s)-1)     # with a space string 1 shorter than s
        ,i)                      # in i
      ),
      s)                         # split string
  ,'')

Test run

> N=nchar;i=scan(,'');for(s in scan(,'',t='NBM BM NB M B N'))cat(paste0(N(i)-N(i<-gsub(s,strtrim('  ',N(s)-1),i)),s),'')
1: NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM
2: 
Read 1 item
Read 6 items
3NBM 8BM 5NB 14M 6B 17N 
> N=nchar;i=scan(,'');for(s in scan(,'',t='NBM BM NB M B N'))cat(paste0(N(i)-N(i<-gsub(s,strtrim('  ',N(s)-1),i)),s),'')
1: NBMMBNBNBM
2: 
Read 1 item
Read 6 items
2NBM 0BM 1NB 1M 1B 0N 
> 
\$\endgroup\$
7
\$\begingroup\$

Pyth, 19 bytes

jd+Ltl=zc`zd_.:"NBM

This is a mixture of @isaacg's Pyth solution and @xnor's incredible Python trick.

Try it online: Demonstration or Test harness

Explanation

jd+Ltl=zc`zd_.:"NBM   implicit: z = input string
             .:"NBM   generate all substrings of "NBM"
            _         invert the order
  +L                  add left to each d in ^ the following:
         `z             convert z to a string
        c  d            split at d
      =z                assign the resulting list to z
    tl                  length - 1
jd                    join by spaces and implicit print
\$\endgroup\$
6
\$\begingroup\$

Julia, 106 97 bytes

b->for s=split("NBM BM NB M B N") print(length(matchall(Regex(s),b)),s," ");b=replace(b,s,".")end

This creates an unnamed function that takes a string as input and prints the result to STDOUT with a single trailing space and no trailing newline. To call it, give it a name, e.g. f=b->....

Ungolfed + explanation:

function f(b)
    # Loop over the creatures, biggest first
    for s = split("NBM BM NB M B N")

        # Get the number of creatures as the count of regex matches
        n = length(matchall(Regex(s), b))

        # Print the number, creature, and a space
        print(n, s, " ")

        # Remove the creature from captivity, replacing with .
        b = replace(b, s, ".")
    end
end

Examples:

julia> f("NBMMBNBNBM")
2NBM 0BM 1NB 1M 1B 0N 

julia> f("NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM")
3NBM 8BM 5NB 14M 6B 17N 
\$\endgroup\$
4
\$\begingroup\$

Python 2, 93 88 89 84 Bytes

Taking the straightforward approach.

def f(n):
 for x in"NBM BM NB M B N".split():print`n.count(x)`+x,;n=n.replace(x,"+")

Call like so:

f("NBMMBNBNBM")

Output is like so:

2NBM 0BM 1NB 1M 1B 0N
\$\endgroup\$
  • \$\begingroup\$ You can remove the space after in. \$\endgroup\$ – isaacg Jun 18 '15 at 4:59
  • \$\begingroup\$ In Python 2, you can convert to a string representation with `x`. \$\endgroup\$ – xnor Jun 18 '15 at 8:28
4
\$\begingroup\$

SAS, 144 142 139 129

data;i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;

Usage (7 bytes added for sysparm):

$ sas -stdio -sysparm NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM << _S
data;i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;
_S

or

%macro f(i);i="&i";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1‌​,i);put a+(-1)z@;end;%mend;

Usage:

data;%f(NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM)

Result:

3NBM 5NB 8BM 17N 6B 14M
\$\endgroup\$
  • \$\begingroup\$ You can save a couple bytes using cats('s/',z,'/x/') in place of 's/'||strip(z)||'/x/'. \$\endgroup\$ – Alex A. Jun 18 '15 at 3:59
  • 1
    \$\begingroup\$ Nice, that was quite a trip back to 139 :) \$\endgroup\$ – Fried Egg Jun 18 '15 at 4:16
  • 1
    \$\begingroup\$ 126 bytes: macro a i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;% \$\endgroup\$ – Alex A. Jun 18 '15 at 14:51
  • 1
    \$\begingroup\$ 122: data;i="&sysparm";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;. Since you're already reading from sysparm, you may as well just run it as a data step. And if you're running in batch, you don't need run;. \$\endgroup\$ – Alex A. Jun 18 '15 at 17:19
  • 1
    \$\begingroup\$ But you could get 129 by using a modern style macro which doesn't read from the command line argument: %macro a(i);i="&i";do z='NBM','NB','BM','N','B','M';a=count(i,z,'t');i=prxchange(cats('s/',z,'/x/'),-1,i);put a+(-1)z@;end;%mend; \$\endgroup\$ – Alex A. Jun 18 '15 at 17:32
3
\$\begingroup\$

PHP4.1, 92 bytes

Not the shortest one, but what else would you expect from PHP?

To use it, set a key on a COOKIE, POST, GET, SESSION...

<?foreach(split(o,NBMoNBoBMoMoBoN)as$a){echo count($T=split($a,$S))-1,"$a ";$S=join('',$T);}

The apporach is basic:

  • Split the string into the names of the creatures
  • Count how many elements there are
  • Subtract 1 (an empty string would give an array with 1 element)
  • Output the count and the creature name
  • Join it all together, using an empty string (which will reduce the string and remove the last creature)

Easy, right?

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

JavaScript, 108 116 bytes

Just a straight forward approach, nothing fancy

o="";r=/NBM|NB|BM|[NMB]/g;g={};for(k in d=(r+prompt()).match(r))g[d[k]]=~-g[d[k]];for(k in g)o+=~g[k]+k+" ";alert(o);
\$\endgroup\$
  • 1
    \$\begingroup\$ Does not work: All 6 counts must be shown, separated by spaces, even when they are 0.. Test case: N \$\endgroup\$ – edc65 Jun 18 '15 at 13:16
  • \$\begingroup\$ @edc65 Woah. I just missed that part. Thanks for pointing that out. Fixed it for the cost of 8chars \$\endgroup\$ – C5H8NNaO4 Jun 18 '15 at 13:37
2
\$\begingroup\$

Perl, 46

#!perl -p
$_="NBM BM NB M B N"=~s/\w+/~~s!$&!x!g.$&/ger
\$\endgroup\$
  • \$\begingroup\$ Explanation on how this works? \$\endgroup\$ – Cain Jun 18 '15 at 21:39
1
\$\begingroup\$

SpecBAS - 164

1 INPUT s$
2 FOR EACH a$ IN ["NBM","BM","NB","M","B","N"]
3 LET n=0
4 IF POS(a$,s$)>0 THEN INC n: LET s$=REPLACE$(s$,a$,"-"): GO TO 4: END IF
5 PRINT n;a$;" ";
6 NEXT a$

Uses the same approach as a lot of others. Line 4 keeps looping over the string (from largest first), replaces it if found.

SpecBAS has some nice touches over original ZX/Sinclair BASIC (looping through lists, finding characters) which I'm still finding out.

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

C, 205 186 184 bytes

A little different approach based on state machine. where t is the state.

a[7],t,i;c(char*s){do{i=0;t=*s==78?i=t,1:*s-66?*s-77?t:t-4?t-2?i=t,3:5:6:t-1?i=t,2:4;i=*s?i:t;a[i]++;}while(*s++);printf("%dN %dB %dM %dNB %dBM %dNBM",a[1],a[2],a[3],a[4],a[5],a[6]);}

Expanded

int a[7],t,i;

void c(char *s)
{
    do {
        i = 0;
        if (*s == 'N') {
            i=t; t=1;
        }
        if (*s == 'B') {
            if (t==1) {
                t=4;
            } else {
                i=t;
                t=2;
            }
        }
        if (*s == 'M') {
            if (t==4) {
                t=6;
            } else if (t==2) {
                t=5;
            } else {
                i=t;
                t=3;
            }
        }
        if (!*s)
            i = t;
        a[i]++;
    } while (*s++);
    printf("%dN %dB %dM %dNB %dBM %dNBM",a[1],a[2],a[3],a[4],a[5],a[6]);
}

Test function

#include <stdio.h>
#include <stdlib.h>

/*
 * 0 : nothing
 * 1 : N
 * 2 : B
 * 3 : M
 * 4 : NB
 * 5 : BM
 * 6 : NBM
 */
#include "nbm-func.c"

int main(int argc, char **argv)
{
    c(argv[1]);
}
\$\endgroup\$
  • \$\begingroup\$ Wouldn't using for(;;*s++){...} instead of do{...}while(*s++); save some bytes? Also, you don't need the newline character in the printf. \$\endgroup\$ – Spikatrix Jun 18 '15 at 8:22
  • \$\begingroup\$ I think you meant for(;*s;s++). But I needed to loop with that last null character. Good call on saving the \n, which is not required. \$\endgroup\$ – some user Jun 18 '15 at 15:19
1
\$\begingroup\$

C, 146

f(char*s)
{
  char*p,*q="NBM\0NB\0BM\0N\0B\0M",i=0,a=2;
  for(;i<6;q+=a+2,a=i++<2)
  {
    int n=0;
    for(;p=strstr(s,q);++n)*p=p[a>1]=p[a]=1;
    printf("%d%s ",n,q);
  }
}

// Main function, just for testing
main(c,a)char**a;{
  f(a[1]);
}  
\$\endgroup\$
1
\$\begingroup\$

Haskell - 177 bytes (without imports)

n s=c$map(\x->(show$length$filter(==x)(words$c$zipWith(:)s([f(a:[b])|(a,b)<-zip s(tail s)]++[" "])))++x++" ")l
f"NB"=""
f"BM"=""
f p=" "
l=["N","B","M","NB","BM","NBM"]
c=concat

(Sorry for the internet necromancy here.)

The Haskell Platform doesn't have string search without imports, and I wanted to show off and exploit the fact that the searched strings are all substrings of one (without repetitions), so that grouping characters can be done by identifying pairs that are allowed to follow each other, which is what f does here.

I still need the full list l in the end to check for equality and display exactly as required, but would not, had the challenge only been to report the number of occurrences of the possible words in any order.

\$\endgroup\$
0
\$\begingroup\$

Bash - 101

I=$1
for p in NBM BM NB M B N;{ c=;while [[ $I =~ $p ]];do I=${I/$p/ };c+=1;done;echo -n ${#c}$p\ ;}

Pass the string as the first argument.

bash nmb.sh MBNNBBMNBM 

Explained a bit:

# We have to save the input into a variable since we modify it.
I=$1

# For each pattern (p) in order of precedence
for p in NBM BM NB M B N;do
    # Reset c to an empty string
    c=

    # Regexp search for pattern in string
    while [[ $I =~ $p ]];do
        # Replace first occurance of pattern with a space
        I=${I/$p/ }
        # Append to string c. the 1 is not special it could be any other
        # single character
        c+=1
    done

    # -n Suppress's newlines while echoing
    # ${#c} is the length on the string c
    # Use a backslash escape to put a space in the string.
    # Not using quotes in the golfed version saves a byte.
    echo -n "${#c}$p\ "
done
\$\endgroup\$
0
\$\begingroup\$

rs, 275 bytes

(NBM)|(NB)|(BM)|(N)|(B)|(M)/a\1bc\2de\3fg\4hi\5jk\6l
[A-Z]+/_
#
+(#.*?)a_b/A\1
+(#.*?)c_d/B\1
+(#.*?)e_f/C\1
+(#.*?)g_h/D\1
+(#.*?)i_j/E\1
+(#.*?)k_l/F\1
#.*/
#
#(A*)/(^^\1)NBM #
#(B*)/(^^\1)NB #
#(C*)/(^^\1)BM #
#(D*)/(^^\1)N #
#(E*)/(^^\1)B #
#(F*)/(^^\1)M #
\(\^\^\)/0
 #/

Live demo and tests.

The workings are simple but a little odd:

(NBM)|(NB)|(BM)|(N)|(B)|(M)/a\1bc\2de\3fg\4hi\5jk\6l

This creatively uses groups to turn input like:

NBMBM

into

aNBMbcdeBMfghijkl

The next line:

[A-Z]+/_

This replaces the sequences of capital letters with underscores.

#

This simply inserts a pound sign at the beginning of the line.

+(#.*?)a_b/A\1
+(#.*?)c_d/B\1
+(#.*?)e_f/C\1
+(#.*?)g_h/D\1
+(#.*?)i_j/E\1
+(#.*?)k_l/F\1
#.*/

This is the beginning cool part. It basically takes the sequences of lowercase letters and underscores, converts them into capital letters, groups them together, and places them before the pound that was inserted. The purpose of the pound is to manage the sequences that have already been processed.

#

The pound is re-inserted at the beginning of the line.

#(A*)/(^^\1)NBM #
#(B*)/(^^\1)NB #
#(C*)/(^^\1)BM #
#(D*)/(^^\1)N #
#(E*)/(^^\1)B #
#(F*)/(^^\1)M #
\(\^\^\)/0
 #/

The capital letters are replaced by their text equivalents with the associated counts. Because of a bug in rs (I didn't want to risk fixing it and getting disqualified), the empty sequences are converted into (^^), which is replaced by a 0 in the second-to-last line. The very last line simply removes the pound.

\$\endgroup\$
0
\$\begingroup\$

KDB(Q), 76 bytes

{" "sv string[-1+count@'enlist[x]{y vs" "sv x}\l],'l:" "vs"NBM NB BM N B M"}

Explanation

                                                   l:" "vs"NBM NB BM N B M"     / substrings
                        enlist[x]{y vs" "sv x}\l                                / replace previous substring with space and cut
              -1+count@'                                                        / counter occurrence
       string[                                  ],'                             / string the count and join to substrings
{" "sv                                                                     }    / concatenate with space, put in lambda

Test

q){" "sv string[-1+count@'enlist[x]{y vs" "sv x}\l],'l:" "vs"NBM NB BM N B M"}"NNNMNBMMBMMBMMMNBMNNMNNNBNNNBNBBNBNMMNBBNBMMBBMBMBBBNNMBMBMMNNNNNMMBMMBM"
"3NBM 5NB 8BM 17N 6B 14M"
q){" "sv string[-1+count@'enlist[x]{y vs" "sv x}\l],'l:" "vs"NBM NB BM N B M"}""
"0NBM 0NB 0BM 0N 0B 0M"
\$\endgroup\$
0
\$\begingroup\$

Haskell: 244 bytes

import Data.List
s="NBM"
[]#_=[[]]
a#[]=[]:a#s
l@(a:r)#(b:m)
 |a==b=let(x:y)=r#m in((a:x):y)
 |True=[]:l#m
c?t=length$filter(==t)c
p=["N","B","M","NB","BM","NBM"]
main=getLine>>= \l->putStrLn.intercalate " "$map(\t->show((l#[])?t)++t)p
\$\endgroup\$
  • \$\begingroup\$ Some suggestions: You're using p and s only once, so there's no need to give it a name (-> a#[]=[]:a#"NBM", same for p). BTW: words"N B M NB BM NBM" instead of the list of strings saves additional bytes. The import is only for intercalate, it's shorter te re-implement it: ...putStrLn.tail.((' ':)=<<)$map... and get rid of the import. Put the all the guards | in the definition of # in a single line and use 1<2 instead of True: ...#(b:m)|a==b=...l#m|1<2=[]... ... \$\endgroup\$ – nimi Jul 17 '15 at 13:34
  • \$\begingroup\$ ... ? can be defined shorter with a list comprehension: c?t=sum[1|x<-c,x==t]. Again, you're using ? only once, so use the body directly: ...show(sum[1|x<-l#[],x==t]). \$\endgroup\$ – nimi Jul 17 '15 at 13:34

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.