FizzBu​​zz-ish字符串匹配器


25

假设您有一个像这样的字符串:

abaabbbbbaabba

计算指定字符在输入字符串中出现的次数,但前提是该字符仅连续出现一次。例如,如果字符是a

abaabbbbbaabba
^ x      x   ^

总数为2(aa因为a连续出现两次,所以不会计数)。

这与FizzBu​​zz有什么关系?

如果字符连续出现3次(或3的倍数),或连续出现5次(或5的倍数),则计数器递减。如果它是3 倍 5倍的倍数,则计数器仍会递增。请记住,如果字符仅连续出现一次,则计数器也会增加;如果字符连续出现其他次数,则计数器将被忽略(上述情况除外)。

回顾一下,如果要匹配的字符串是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)

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);
    }   
}
  • 将搜索的字符串可以是任意长度,但是模式只能是单个字符。
  • 这两个字符串都不会包含正则表达式特殊字符。
  • 这是;以字节为单位的最短程序获胜。
  • 没有标准漏洞。

3
如果您可以提供更多测试示例,这将很有用。特别是序列中包含多个字母的序列。
Reto Koradi

我添加了一些案例,希望能有所帮助。告诉我是否需要更多案件-这是我第一次在PPCG工作。
丹尼尔·M.

我将更改要求,以使序列仅是一个字符,因为实现方式几乎相同,但不那么混乱。
Daniel M.

这就像1稀疏问题,但带有FizzBu​​zz附加功能
ev3commander

Answers:


32

功能,1840字节

该死的,这种语言是行不通的。

该程序希望输入的第一个字符是要搜索的字符,其余的输入则是要搜索字符串的字符。这意味着aaabaa在输入中进行搜索aaba(因此将在输出1中进行搜索)。您可以使用换行符或空格(a aaba)分隔它们,但这仅是因为多余的换行符/空格对输出没有影响。

与往常一样,如果$('pre').css('line-height',1)在浏览器控制台中执行,则可以获得更好看的渲染(没有行距)。

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

(编码为UTF-16时为1840字节。)

说明

  • ¹ 返回字符串的第一个字符。
  • ²计算给定字符串开始处字符出现的次数。例如,给定字符a和字符串aaba,它将返回2。对于abaa,它将返回0。
  • ³调用²以获取开始的字符数,检查该数字是否可被3和5整除,以及该数字是否等于1,并确定适当的增/减。它还从字符串的开头删除了一个多余的字符(例如,给定aaabba它删除了3 + 1 = 4个字符,给出ba)。然后,它使用较短的字符串递归调用自身并添加结果。
  • 主程序调用¹以从输入中删除第一个字符,然后³以该字符和字符串的其余部分作为单独的参数进行调用。

10
我永远不会支持Funciton。
orlp 2015年

14

CJam,40 36 35 32 30字节

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

感谢@MartinBüttner打高尔夫球1个字节!

感谢@AndreaBiondo打出2个字节并为另外3个字节铺平了道路!

CJam解释器中在线尝试。

怎么运行的

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#

您还可以使用基本编码的查找表和模块化索引来节省另外2个字节:llcf=e`::*0-{(_!\6563282Zb:(=}%1b33个字节。
Andrea Biondo

@AndreaBiondo实际上节省了3个字节。谢谢!
丹尼斯

7

C,160 126 125 119 114 109 104 100字节

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

可能可以做得更好...这需要命令行参数的输入(第一个参数是模式,第二个参数是字符串)。不支持搜索NULL char(\ x00)模式。

编辑** 126 125 119 114 109 104 100字节**:合并了丹尼斯的建议和一些其他想法(删除了else子句,将while合并为一个语句,并使用减法代替了!=)。还删除了for循环中多余的分号(这实际上是Dennis的建议的一部分)。通过删除变量“ i”和“ 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);}

通过滥用三元运算符,删除了if和neg('!')运算符。通过使用按位“ AND”欺骗双精度&& 来压缩模块化检查,因为按位“&”具有错误,然后将(t <2)比较放入三元运算符中。通过将!! t移至三元运算符来替换!! t *(...),从而使我可以删除括号。

伙计,我真的很想让它低于100字节标记: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);}

暂定的解决方案:我不确定这些是否有效,但是如果我使用exit(s)而不是printf(“%d”,s),则可以减少到93个字符。但是,输出将不可见,而是返回代码。如果确实需要输出,我还可以将其缩减为98字节,但是这也需要在最终答案之前打印s的所有中间值...


3
欢迎来到编程难题和代码高尔夫球!我还没有对它进行彻底的测试,但是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);}应该也能很好地工作(它短了23个字节)。
丹尼斯

哦,把if(){}子句变成一个语句真是太好了!
Tob Ernack

一些更多的字节:如果你开始main使用for(a=*z[1];c;i++),你不需要{}周围的IF ... ELSE。
丹尼斯


4

Python 3中,361,300,296,263,256,237,229,188,178,164个字节。

由于使用了SOPython的vaultah,节省了15个字节。
感谢SOPython的Joe Kington,节省了9个字节。
由于使用了SOPython的DSM,节省了11个字节。

这是我第一次提交答案,所以我相信这可能会短得多。它以测试字符串作为输入的第一个响应,以搜索字符作为第二个响应。

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

非高尔夫版本:

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

发现我没有通过其中一个测试用例。


3

Haskell,120字节

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 做这份工作。


3

Java中,146个 152 143 138 139 136字节

  1. 修复了一个错误。
  2. 转移操作,切换到按位运算符进行%3&%5检查。
  3. 缩短i<2比较。
  4. 修复了一个错误(%3&%5检查未按预期工作)。
  5. 使用乘法快捷方式,如@ w0lf的Ruby答案所示。

实现为BiFunction<String, String, Integer>Java中8,让我知道,如果这是必需的是一个完整的程序(或者,如果我甚至可以删除java.util.regex以下包前缀)。

上面的字节数不包括下面的换行符,只是为了在本网站上格式化而添加的换行符。

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

粗略解释:

  1. 匹配的模式应用正则表达式b,即"[^"+b+"]"
  2. 获取每个令牌的长度(例如"a" -> 1)。
  3. 应用所需的映射-101
  4. sum() 得到答案。

2

Javascript,206字节

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}

展开:

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

说明:

我使用正则表达式计算一个字符出现的总次数,然后从中减去所有出现在组中的次数。最后,我遍历各个组并进行嘶嘶声的增加/减少。

通过问题中给出的测试用例:

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

等等


删除new,使用exec代替match和别名length,您应该会很好。
Mama Fun Roll

2

Perl,82 65 63 59字节

58个字节+ 1个字节的命令行参数

并不是特别短,但这只是一个开始-将继续缩短它。

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

假设-i可以用来给输入字符串一个示例用法如下:

echo "aaabaaa" | perl -pi"a" entry.pl


0

盖克,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""

输入为“字符空间字符串”,就像这样

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

不打高尔夫球

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

0

Pyth,27个字节

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

测试套件

输入形式如下:

a
aaaba

说明:

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.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.