Le FizzBuzz-ish String matcher

25

Disons que vous avez une chaîne comme celle-ci:

abaabbbbbaabba

Comptez le nombre de fois qu'un caractère spécifié apparaît dans la chaîne d'entrée, mais uniquement si le caractère n'apparaît qu'une seule fois de suite . Par exemple, si le caractère est a,

abaabbbbbaabba
^ x      x   ^

Le total serait de 2 (le aane compterait pas car le aapparaît deux fois de suite).

Quel est le lien avec FizzBuzz?

Si le caractère apparaît 3 (ou un multiple de 3) fois de suite, ou 5 (ou un multiple de 5) fois de suite, le compteur est décrémenté à la place. S'il s'agit d'un multiple de 3 et 5 fois, le compteur est toujours incrémenté. N'oubliez pas que le compteur est également incrémenté si le caractère n'apparaît qu'une seule fois de suite et il est ignoré si le caractère apparaît un autre nombre de fois de suite (en plus des situations décrites ci-dessus).

Pour récapituler, si la chaîne à faire correspondre est 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)

Implémentation de référence (non golfée) en 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);
    }   
}
  • La chaîne qui sera recherchée peut être de n'importe quelle longueur, mais le motif ne sera qu'un seul caractère.
  • Aucune des deux chaînes n'aura de caractères spéciaux regex.
  • C'est ; le programme le plus court en octets gagne.
  • Pas de failles standard.
Daniel M.
la source
3
Il serait utile de fournir quelques exemples de test supplémentaires. Particulièrement ceux où la séquence a plus d'une lettre.
Reto Koradi
J'ai ajouté quelques cas - j'espère que cela aide. Dites-moi si j'ai besoin de plus de cas, c'est ma première fois au PPCG.
Daniel M.
Je vais modifier les exigences afin que la séquence ne soit qu'un seul caractère, car l'implémentation est à peu près la même, mais moins confuse.
Daniel M.
C'est comme la question à 1 élément mais avec l'ajout de
FizzBuzz

Réponses:

32

Funciton , 1840 octets

Merde, cette langue est impassible.

Ce programme s'attend à ce que le premier caractère de l'entrée soit le caractère à rechercher et le reste de l'entrée à effectuer la recherche dans la chaîne. Cela signifie que aaabava rechercher adans l'entrée aaba(et donc la sortie 1). Vous pouvez les séparer avec une nouvelle ligne ou un espace ( a aaba) mais uniquement parce que la nouvelle ligne / l'espace supplémentaire ne fait aucune différence pour la sortie.

Comme toujours, vous pouvez obtenir un rendu plus joli (sans l'espacement des lignes) si vous exécutez $('pre').css('line-height',1)dans la console de votre navigateur.

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

(1840 octets en cas de codage UTF-16.)

Explication

  • ¹ renvoie le premier caractère d'une chaîne.
  • ²compte le nombre d'occurrences d'un caractère au début d'une chaîne donnée. Par exemple, étant donné le caractère aet la chaîne aaba, il renvoie 2. Pour aet baa, il renvoie 0.
  • ³appelle ²pour obtenir le nombre de caractères au début, examine si le nombre est divisible par 3 et 5 et s'il est égal à 1 et détermine l'incrément / décrément approprié. Il supprime également un caractère supplémentaire au début de la chaîne (par exemple, étant donné aaabbaqu'il supprime 3 + 1 = 4 caractères, ce qui donne ba). Ensuite, il s'appelle récursivement avec la chaîne la plus courte et ajoute le résultat.
  • Le programme principal appelle ¹pour supprimer le premier caractère de l'entrée et appelle ³avec ce caractère et le reste de la chaîne comme arguments séparés.
Timwi
la source
10
Je ne voterai jamais Funciton.
orlp
14

CJam, 40 36 35 32 30 octets

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

Merci à @ MartinBüttner pour avoir joué au golf sur 1 octet!

Merci à @AndreaBiondo d'avoir joué au golf sur 2 octets et ouvert la voie à 3 autres!

Essayez-le en ligne dans l' interpréteur CJam .

Comment ça marche

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#
Dennis
la source
Vous pouvez enregistrer 2 autres octets avec une table de recherche codée en base et une indexation modulaire: llcf=e`::*0-{(_!\6563282Zb:(=}%1b33 octets.
Andrea Biondo
@AndreaBiondo Cela a en fait économisé 3 octets. Merci!
Dennis
7

C, 160 126 125 119 114 109 104 100 octets

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);}

Peut probablement être amélioré ... Cela prend une entrée à partir des arguments de la ligne de commande (le premier argument est le modèle, le second est la chaîne). Ne prend pas en charge la recherche de modèle de caractère NULL (\ x00).

EDIT ** 126 125 119 114 109 104 100 octets **: Après avoir intégré les suggestions de Dennis, et quelques idées supplémentaires (clause supprimé autre, combiné le tout en une seule instruction et soustraction utilisé au lieu de =!). Suppression du point-virgule supplémentaire dans la boucle for (cela faisait en fait partie de la suggestion de Dennis). Raccourci encore plus en supprimant les variables «i» et «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);}

Suppression des opérateurs if et negation ('!') En abusant de l'opérateur ternaire. Compression des vérifications de modularité en utilisant ce «ET» au niveau du bit un double && parce que «&» au niveau du bit a un bogue, et en mettant la comparaison (t <2) à l'intérieur des opérateurs ternaires. Remplacé !! t * (...) en déplaçant !! t dans l'opérateur ternaire, me permettant ainsi de supprimer les parenthèses.

Mec, je veux vraiment l'avoir en dessous de la marque des 100 octets: 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);}

Solutions TENTATIVES: Je ne suis pas sûr que celles-ci soient considérées comme valides, mais je peux descendre à 93 caractères si j'utilise exit (s) au lieu de printf ("% d", s). Mais alors la sortie ne serait pas visible, ce serait plutôt un code retour. Si la sortie est vraiment nécessaire, je peux également la réduire à 98 octets, mais cela nécessiterait également l'impression de toutes les valeurs intermédiaires de s avant la réponse finale ...

Tob Ernack
la source
3
Bienvenue sur Programmation Puzzles & Code Golf! Je ne l'ai pas testé à fond, mais 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);}devrait tout aussi bien fonctionner (et c'est 23 octets de moins).
Dennis
Oh, c'est une bonne chose de transformer la clause if () {} en une seule instruction!
Tob Ernack
Quelques octets de plus: si vous commencez mainpar for(a=*z[1];c;i++), vous n'avez pas besoin de {}contourner le si ... sinon.
Dennis
4

Rubis, 111103 96 octets

->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)}}

Ce défi a été fait pour Ruby Enumerable#chunk, j'ai donc dû le poster. :)

Test en ligne: http://ideone.com/pG4mAn

Le code est assez simple. Voici une version plus lisible: http://ideone.com/ub3siA .

Cristian Lupascu
la source
4

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

15 octets enregistrés grâce à vaultah de SOPython.
9 octets enregistrés grâce à Joe Kington de SOPython.
Sauvegardé 11 octets grâce à DSM de SOPython.

C'est la première fois que je soumets une réponse, donc je suis sûr que cela pourrait être beaucoup plus court. Il prend la chaîne de test comme première réponse à l'entrée et le caractère de recherche comme deuxième.

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

Version non golfée:

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

J'ai découvert que j'échouais l'un des cas de test.

Morgan Thrapp
la source
3

Haskell, 120 octets

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 Fait le travail.

Leif Willerts
la source
3

Java, 146 152 143 138 139 136 octets

  1. Correction d'un bug.
  2. opérations décalées, commutées en opérateur au niveau du bit pour les %3&%5vérifications.
  3. i<2Comparaison raccourcie .
  4. Correction d'un bug (la %3&%5vérification ne fonctionnait pas comme prévu).
  5. Raccourci de multiplication utilisé comme vu dans la réponse Ruby de @ w0lf .

Mis en œuvre en tant que BiFunction<String, String, Integer>dans Java 8, faites-moi savoir si cela doit être un programme complet (ou si je peux même supprimer le java.util.regexpréfixe du package ci-dessous).

Le nombre d'octets ci-dessus n'inclut pas la nouvelle ligne ci-dessous, qui est simplement ajoutée à des fins de formatage sur ce 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();

Explication grossière:

  1. Appliquer l'expression régulière avec un motif qui ne correspond pasb , c'est-à-dire "[^"+b+"]".
  2. Obtenez la longueur de chaque jeton (par exemple "a" -> 1).
  3. Appliquez le mappage souhaité à -1, 0et 1.
  4. sum() pour obtenir une réponse.
hjk
la source
2

Javascript, 206 octets

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}

Étendu:

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;
};

Explication:

J'utilise l'expression régulière pour compter le nombre total de fois qu'un personnage apparaît, puis soustraire de cela toutes les fois où il est apparu en groupes. Enfin, je passe par les groupes et fais l'incrémentation / décrémentation du fizz buzz.

Réussit les cas de test donnés dans la question:

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

etc

je suis sérieux
la source
Supprimez le new, utilisez à la execplace de matchet alias length, et vous devriez être bon.
Mama Fun Roll
2

Perl, 82 65 63 59 octets

58 octets + 1 octet paramètre de ligne de commande

Pas particulièrement court, mais c'est un début - continuera de le raccourcir.

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

En supposant que vous -ipouvez utiliser pour donner à la chaîne d'entrée un exemple d'utilisation, procédez comme suit:

echo "aaabaaa" | perl -pi"a" entry.pl
Jarmex
la source
0

Pyth, 32 octets

si proche! 2 octets de plus pour égaler l'excellente entrée CJam de Dennis

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

Testez-le en ligne

Brian Tuck
la source
0

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""

Entrez comme "chaîne d'espace de caractères", comme ceci

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""'

Non golfé

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
Cabbie407
la source
0

Pyth, 27 octets

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

Suite de tests

Entrez dans le formulaire, par exemple:

a
aaaba

Explication:

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.
isaacg
la source