24
\$\begingroup\$

Say that you have a string like this:

abaabbbbbaabba

Count the number of times that a specified character appears in the input string, but only if the character appears only once in a row. For example, if the character is a,

abaabbbbbaabba
^ x      x   ^

The total would be 2 (the aa's wouldn't count because the a appears two times in a row).

How is this related to FizzBuzz?

If the character appears 3 (or a multiple of 3) times in a row, or 5 (or a multiple of 5) times in a row, the counter is decremented instead. If it is a multiple of both 3 and 5 times, the counter is still incremented. Remember that the counter is also incremented if the character appears only once in a row, and it is ignored if the character appears any other number of times in a row (besides the situations described above).

To recap, if the string to match is a,

input            counter (explanation)

a                 1 (single occurence)
aaa               -1(multiple of 3)
aaaaa             -1(multiple of 5)  
aaaaaaaaaaaaaaa   1 (multiple of 15)
aa                0 (none of the above)

aba               2 (two single instances)
aaba              1 (one single occurence(+1) and one double occurence(ignored))
aaaba             0 (one single occurence(+1) and one triple (-1)
aaaaaa            -1 (six is a multiple of three)

Reference (ungolfed) implementation in java:

import java.util.Scanner;
import java.util.regex.*;

public class StrMatcher {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in); //Scanner to get user input
        int total = 0;//Running total of matches

        System.out.println("Enter a string: ");
        String strBeingSearched = sc.nextLine(); //String that will be searched

        System.out.println("Enter string to match with: ");
        String strBeingMatched = sc.nextLine(); //Substring used for searching

        //Simple regex matcher
        Pattern pattern = Pattern.compile("(" + strBeingMatched + ")+");
        Matcher matcher = pattern.matcher(strBeingSearched);

        while(matcher.find()){  //While there are still matches

            int length = matcher.end() - matcher.start();
            int numberOfTimes = length/strBeingMatched.length();//Calculate how many times in a row the string is matched

            if((numberOfTimes == 1)||((numberOfTimes % 3 == 0) && (numberOfTimes % 5 == 0))){
                total++; //Increment counter if single match or divisible by 15
            } else if((numberOfTimes % 3 == 0)||(numberOfTimes % 5 == 0)) {
                total--; //Decrement counter if divisible by 3 or 5 (but not 15)
            }

            strBeingSearched = strBeingSearched.substring(matcher.end());
            matcher = pattern.matcher(strBeingSearched); //Replace string/matcher and repeat
        }

        System.out.println(total);
    }   
}
  • The string that will be searched can be any length, but the pattern will only be a single character.
  • Neither string will have regex special characters.
  • This is ; shortest program in bytes wins.
  • No standard loopholes.
\$\endgroup\$
4
  • 3
    \$\begingroup\$ It would be useful if you could provide a few more test examples. Particularly ones where the sequence has more than one letter. \$\endgroup\$ Sep 21, 2015 at 1:38
  • \$\begingroup\$ I added a few cases- hopefully that helps. Tell me if I need more cases- it's my first time in PPCG. \$\endgroup\$
    – Daniel M.
    Sep 21, 2015 at 1:49
  • \$\begingroup\$ I'll change the requirements so that the sequence is only a single character, as the implementation is pretty much the same, but less confusing. \$\endgroup\$
    – Daniel M.
    Sep 21, 2015 at 2:09
  • \$\begingroup\$ This is like the 1-sparse question but with the FizzBuzz addition \$\endgroup\$
    – user46167
    Oct 20, 2015 at 20:02

12 Answers 12

32
\$\begingroup\$

Funciton, 1840 bytes

Damn, this language is ungolfable.

This program expects the first character of the input to be the character to search for, and the rest of the input to make the string to search through. This means that aaaba will search for a in the input aaba (and thus output 1). You can separate them with a newline or space (a aaba) but only because the extra newline/space makes no difference to the output.

As always, you can get a nicer-looking rendering (without the line spacing) if you execute $('pre').css('line-height',1) in your browser console.

      ┌───┐
      │╓─╖└─────────────┐
      └╢³╟┐    ┌─────┐ ┌┴┐╓─╖
┌─────┐╙─╜└────┤┌─╖ ┌┴╖│┌┘║¹║
│     ├───────┐└┤²╟─┤·╟┘│ ╙┬╜╔═══════╗
│    ┌┴╖╔═╗┌─╖├┐╘╤╝ ╘╤╝┌┘  └┬╢2097151║
│    │♭║║5╟┤%╟┘└─┴──┐│┌┘┌───┘╚═══════╝
│    ╘╤╝╚═╝╘╤╝╔═╗┌─╖│││┌┴┐┌────┐
│    ┌┴╖   ┌┘ ║3╟┤%╟┘││└┬┘│╔══╗└┐
│  ┌─┤·╟─┐ │  ╚═╝╘╤╝ │└┐  │║21╟┐│
│  │ ╘╤╝ ├─┘┌─────┘  └┐└┐ │╚══╝│└─┐
│ ┌┴╖┌┴╖┌┴╖┌┴╖┌─╖    ┌┴╖│ │┌─╖┌┴─╖│
│┌┤·╟┤?╟┤?╟┤?╟┤+╟────┤³║│ └┤²╟┤>>║└──┐
││╘╤╝╘╤╝╘╤╝╘╤╝╘╤╝    ╘╤╝│  ╘╤╝╘╤═╝╓─╖│
││ │ ┌┴╖┌┴╖┌┴╖┌┴╖╔═╗ ┌┴╖│  ┌┴╖ ├──╢²╟┤
││ └─┤·╟┤·╟┤?╟┤·╟╢1║┌┤·╟┘  │♯║┌┴╖ ╙─╜│
│└──┐╘╤╝╘╤╝╘╤╝╘╤╝╚═╝│╘╤╝   ╘╤╝│¹║┌───┘
└──┐│╔╧╗ └┬─┘ ┌┴╖   │┌┴─╖   │ ╘╤╝│
   ││║1║ ┌┴┐┌─┤?╟───┴┤>>╟┐ ┌┴╖┌┴╖│
   ││╚═╝ └┬┘│ ╘╤╝    ╘══╝│┌┤?╟┤=║│
   │└────┐│╔╧╗     ┌─────┘│╘╤╝╘╤╝│
╔═╗└────┐│├╢0║╔══╗┌┴╖┌─╖ ╔╧╗   └─┘
║ ║     │└┘╚═╝║21╟┤×╟┤♯╟┐║0║
╚╤╝     └──┐  ╚══╝╘═╝╘═╝│╚═╝
 │┌──┴────╖└────────────┘
 ││int→str║
 │╘══╤════╝
┌┴─╖┌┴╖┌─╖╔╗
│>>╟┤³╟┤¹╟╢║
╘═╤╝╘═╝╘═╝╚╝
╔═╧╗
║21║
╚══╝

(1840 bytes when encoded as UTF-16.)

Explanation

  • ¹ returns the first character of a string.
  • ² counts the number of occurrences of a character at the start of a given string. For example, given the character a and the string aaba, it returns 2. For a and baa, it returns 0.
  • ³ calls ² to get the number of characters at the start, examines whether the number is divisible by 3 and 5 and whether it is equal to 1 and determines the proper increment/decrement. It also removes one extra character from the start of the string (e.g. given aaabba it removes 3+1=4 characters, giving ba). Then it calls itself recursively with the shorter string and adds the result.
  • The main program calls ¹ to remove the first character from the input and calls ³ with that character and the rest of the string as separate arguments.
\$\endgroup\$
1
  • 10
    \$\begingroup\$ I will never not upvote Funciton. \$\endgroup\$
    – orlp
    Sep 21, 2015 at 16:20
14
\$\begingroup\$

CJam, 40 36 35 32 30 bytes

0llcf=e`::*{(_g+Y13515Yb+=(+}/

Thanks to @MartinBüttner for golfing off 1 byte!

Thanks to @AndreaBiondo for golfing off 2 bytes and paving the way for 3 more!

Try it online in the CJam interpreter.

How it works

0          e# Push a 0 (accumulator).
l          e# Read a line from STDIN.
lc         e# Read a second line and keep only the first character.
f=         e# Check each character from the first line for equality.
           e# This results in 1 for the specified character and 0 for others.
e`         e# Perform run-length encoding.
::*        e# Multiply each element by its number of repetitions.
{          e# For each remaining integer I:
  (_!      e#   Subtract 1, copy and push sign(I-1).
  +        e#   Add the results.
           e#     If I == 0, I-1 + sign(I-1) =  -1 + -1 = -2.
           e#     If I == 1, I-1 + sign(I-1) =   0 +  0 =  0.
           e#     If I >= 2, I-1 + sign(I-1) = I-1 +  1 =  I.
  Y        e#   Push 2.
  13515Yb  e#   Convert 13515 into the array of its binary digits.
  +        e#   Concatenate 2 and the array.
           e#   This pushes [2 1 1 0 1 0 0 1 1 0 0 1 0 1 1].
  =        e#   Retrieve the digit at (index I-1 + sign(I-1))%15.
           e#     If I == 0, this pushes 1.
           e#     Else, if I == 1, this pushes 2.
           e#     Else, if I%15 == 0, this pushes 2.
           e#     Else, if I%3==0 or I%5==0, this pushes 0.
           e#     Else, this pushes 1.
  (        e#   Decrement the result.
  +        e#   Add it to the accumulator.
}/         e#
\$\endgroup\$
2
  • \$\begingroup\$ You can save another 2 bytes with a base-encoded lookup table and modular indexing: llcf=e`::*0-{(_!\6563282Zb:(=}%1b is 33 bytes. \$\endgroup\$ Sep 24, 2015 at 13:41
  • \$\begingroup\$ @AndreaBiondo That actually saved 3 bytes. Thanks! \$\endgroup\$
    – Dennis
    Sep 24, 2015 at 14:57
7
\$\begingroup\$

C, 160 126 125 119 114 109 104 100 bytes

main(int q,char **z){int i=0,t=0,s=0,a=z[1][0],c;do{if((c=z[2][i])!=a){s+=(!!t)*((t==1)-!(t%3)-!(t%5)+3*!(t%15));t=0;}else{++t;}++i;}while(c);printf("%d\n",s);}

Probably can be made better... This takes input from command line arguments (first argument is pattern, second is string). Does not support searching for pattern of NULL char (\x00).

EDIT **126 125 119 114 109 104 100 bytes **: After incorporating Dennis's suggestions, and some additional ideas (removed else clause, combined the while into one single statement and used subtraction instead of !=). Also removed extra semicolon in for loop (that was actually part of Dennis's suggestion). Shortened even more by removing the variables 'i' and 'a'.

t,s;main(c,z)char**z;{for(;c;t++)if((c=*z[2]++)-*z[1])s+=!!t*((t<2)-!(t%3)-!(t%5)+3*!(t%15)),t=-1;printf("%d",s);}

Removed the if and negation ('!') operators by abusing the ternary operator. Compressed the modularity checks by using that bitwise 'AND' trick a double && because bitwise '&' has a bug, and putting the (t<2) comparison inside the ternary operators. Replaced !!t * (...) by moving !!t into the ternary operator, thus allowing me to remove parentheses.

Man, I really want to get it below the 100 byte mark :S

t,s;main(c,z)char**z;{for(;c;)(c=*z[2]++)-*z[1]?s+=t%15?t%3&&t%5?t<2:-1:!!t,t=0:t++;printf("%d",s);}

TENTATIVE solutions: I am not sure if these would be considered valid, but I can get down to 93 chars if I use exit(s) instead of printf("%d",s). But then the output would not be visible, rather it would be a return code. If output is really necessary, I can also get it down to 98 bytes, but it would require printing all intermediate values of s before the final answer as well...

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Welcome to Programming Puzzles & Code Golf! I haven't tested it thoroughly, but i,t,s,a;main(c,z)char**z;{a=*z[1];while(c){if((c=z[2][i])!=a)s+=(!!t)*((t<2)-!(t%3)-!(t%5)+3*!(t%15)),t=0;else++t;++i;}printf("%d",s);} should work just as well (and it's 23 bytes shorter). \$\endgroup\$
    – Dennis
    Sep 21, 2015 at 5:21
  • \$\begingroup\$ Oh, nice thing with turning the if () { } clause into one statement! \$\endgroup\$
    – Tob Ernack
    Sep 21, 2015 at 5:32
  • \$\begingroup\$ A few more bytes: If you start main with for(a=*z[1];c;i++), you don't need the {} around the if...else. \$\endgroup\$
    – Dennis
    Sep 21, 2015 at 5:34
4
\$\begingroup\$

Ruby, 111 103 96 bytes

->s,m{s.chars.chunk{|x|x}.reduce(0){|x,(c,g)|l=g.size
x+(c!=m ?0:l<2||l%15<1?1:l%3*l%5<1?-1:0)}}

This challenge was made for Ruby's Enumerable#chunk, so I had to post this. :)

Online test: http://ideone.com/pG4mAn

The code is pretty much straightforward. Here's a more readable version: http://ideone.com/ub3siA.

\$\endgroup\$
4
\$\begingroup\$

Python 3, 361, 300, 296, 263, 256, 237, 229, 188, 178, 164 bytes.

Saved 15 bytes thanks to vaultah from SOPython.
Saved 9 bytes thanks to Joe Kington from SOPython.
Saved 11 bytes thanks to DSM from SOPython.

This is my first time submitting an answer, so I'm sure this could be much shorter. It takes the test string as the first response to input, and the search char as the second one.

t=input()
m=input()
c=u=0
g=iter(t)
while g:
 r=next(g,0)
 if r==0:print(c);g=0
 while r==m:u+=1;r=next(g,0)
 if u:b=u%3<1;v=u%5<1;c+=((0,-1)[b|v],1)[u<2or b&v];u=0

Ungolfed version:

import sys
test = sys.argv[1]
match_char = sys.argv[2]
counter = char_counter = 0
char_generator = (c for c in test)
while char_generator:
    try:
        char = next(char_generator)
    except StopIteration:
        print(counter)
        break
    while char == match_char:
        char_counter += 1
        try:
            char = next(char_generator)
        except StopIteration:
            break
    if char_counter == 0:
        continue
    counter += 1 if char_counter == 1 or (char_counter % 3 == 0 and char_counter % 5 == 0) else -1 if char_counter % 3 == 0 or char_counter % 5 == 0 else 0
    char_counter = 0

Discovered I was failing one of the test cases.

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

Haskell, 120 bytes

import Data.List
f c=sum.map(v.length).filter((==c).head).group
v 1=1
v n|n%3&&n%5=1|(n%3||n%5)=(-1)|0<1=0
x%y=x`mod`y<1

f does the job.

\$\endgroup\$
3
\$\begingroup\$

Java, 146 152 143 138 139 136 bytes

  1. Fixed a bug.
  2. shifted operations, switched to bitwise operator for the %3&%5 checks.
  3. Shortened i<2 comparison.
  4. Fixed a bug (%3&%5 check not working as thought).
  5. Used multiplication shortcut as seen in @w0lf's Ruby answer.

Implemented as a BiFunction<String, String, Integer> in Java 8, let me know if this is required to be a full program (or if I can even drop the java.util.regex package prefix below).

Byte count above does not include the newline below, which is simply added for formatting purposes on this site.

(a,b)->java.util.regex.Pattern.compile("[^"+b+"]").splitAsStream(a)
.mapToInt(v->v.length()).map(i->i<2?i:i%15<1?1:i%3*i%5<1?-1:0).sum();

Rough explanation:

  1. Apply regex with pattern that does not match b, i.e. "[^"+b+"]".
  2. Get length of each token (e.g. "a" -> 1).
  3. Apply the desired mapping to -1, 0 and 1.
  4. sum() to get answer.
\$\endgroup\$
2
\$\begingroup\$

Javascript, 206 bytes

function f(n,e){var t=n.match(new RegExp(e,"g")).length,g=n.match(new RegExp(e+"{2,}","g"));return null!==g&&g.forEach(function(n){t-=n.length,n.length%15==0?t+=1:(n.length%3==0||n.length%5==0)&&(t-=1)}),t}

Expanded:

function funkyFizzb(n, c) {
    var score = n.match(new RegExp(c, "g")).length; 
    var repeatOccurence = n.match(new RegExp(c + "{2,}", "g"));

    if(repeatOccurence !== null) {
        repeatOccurence.forEach(function(v,i){
            // remove multiple occurrence counts
            score -= v.length;

            if(v.length % 15 == 0) {
                score += 1;
            }

            else if(v.length % 3 == 0 || v.length % 5 == 0) {
                score -= 1;
            }
        });
    }

    return score;
};

Explanation:

I'm using regex to count total times a character appears, then subtract from that all the times it appeared in groups. Finally, I go through the groups and do the fizz buzz increment / decrement.

Passes the test cases given in the question:

funkyFizzb("aaa", "a") => -1

and so on

\$\endgroup\$
1
  • \$\begingroup\$ Remove the new, use exec instead of match, and alias length, and you should be good. \$\endgroup\$ Nov 10, 2015 at 14:32
2
\$\begingroup\$

Perl, 82 65 63 59 bytes

58 bytes + 1 byte command line parameter

Not particularly short, but it's a start - will continue shortening it.

$l=y///c,$i+=!($l>1&&$l%15)||-!($l%3*$l%5)for/$^I+/g;$_=$i

Assuming -i can be used to give the input string an example usage is as follows:

echo "aaabaaa" | perl -pi"a" entry.pl
\$\endgroup\$
0
\$\begingroup\$

Pyth, 32 bytes

so close! 2 more bytes to tie Dennis' excellent CJam entry

s.b?qYz?tN@+,0_1 1+}3PN}5PN1Zrw8

Test it Online

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

gawk, 140

p=$2{b="[^"$1"]";for($0=2;$i-->0;){sub("^"b"*",_,p);p=substr(p,$++i=match(p,b))}for($i=length(p);$++j;)s+=$j%5?$j%3?$j<2:-1:$j%3?-1:1}$0=s""

Input as "char space string", like so

echo "x axxbxcxdexxxfffghixxj" | awk 'p=$2{b="[^"$1"]";for($0=2;$i-->0;){sub("^"b"*",_,p);p=substr(p,$++i=match(p,b))}for($i=length(p);$++j;)s+=$j%5?$j%3?$j<2:-1:$j%3?-1:1}$0=s""'

Ungolfed

p=$2{
    #i=j=s=0                # make reusable
    b="[^"$1"]";           # pattern "not matching char"
    $0=2;                  # help starting the while loop
    while($i-->0){         # match didn't return -1; dec stack top
        sub("^"b"*",_,p);  # remove not matching chars at head of string
        $++i=match(p,b);   # push index of first occurence of not matching char
        p=substr(p,$i)     # remove matching chars from head of string
    };
    $i=length(p);          # get last value
    while($++j)            # sometimes last value on stack is 0
        s+=$j%5?$j%3?$j<2:-1:$j%3?-1:1

        # if $j%5!=0
        #   if $j%3!=0     (not divisible by 5 AND 3)
        #     s+=($j==1)   (single character)
        #   else           (divisible by 3 but not by 5)
        #     s-=1
        # else             (divisble by 5)
        #   if $j%3!=0
        #     s-=1         (divisible by 5 but not by 3)
        #   else
        #     s+=1         (divisible by 3 AND 5)

}$0=s"" # output
\$\endgroup\$
0
\$\begingroup\$

Pyth, 27 bytes

sm|!JPdx,02+}3J}5JhMf}zTrw8

Test suite

Input in the form e.g.:

a
aaaba

Explanation:

sm|!JPdx,02+}3J}5JhMf}zTrw8
                               z = input() (The match character)
                         w     input() (The string)
                        r 8    Run length encode
                    f}zT       Filter for the runs z is in.
                  hM           Take their lengths
 m|                            Map (d) to the logical or of
    Pd                         Find all prime factors of the current run length
   J                           Save them in J
  !                            Take the logical negation. This will be 1 if
                               d is 1, and 0 otherwise.
           +}3J                If d wasn't 1, add up 1 if 3 is in J
               }5J             and 1 if 5 is in J.
       x,02                    Then, take the index of the result in [0,2]
                               so 0 -> 0, 2 -> 1, 1 -> -1 (not found)
s                              Sum up the values for each run.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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