【问题标题】:Code Golf: Duplicate Character Removal in StringCode Golf:字符串中的重复字符删除
【发布时间】:2010-11-23 13:56:43
【问题描述】:

挑战:字符数的最短代码,用于检测和删除字符串中的重复字符。删除包括重复字符的所有实例(因此,如果您找到 3 个 n,则所有三个都必须删除),并且需要保留原始字符顺序。

示例输入 1:
nbHHkRvrXbvkn

示例输出 1:
RrX


示例输入 2:
nbHHkRbvnrXbvkn

示例输出 2:
RrX

(第二个示例删除了出现三次的字母;一些解决方案未能解决此问题)

(这是基于my other question,我需要在 C# 中以最快的方式执行此操作,但我认为它可以跨语言制作良好的 Code Golf。)

【问题讨论】:

  • 这里肯定会有 10/15 个字符以下的 Perl 解决方案。
  • 4 字符 J 解决方案在哪里?
  • 解决方案应该是一个接受用户输入的完整程序,还是只是一个执行给定任务的函数/方法?
  • 如果它只是一个函数,在大多数情况下它可能会更短。 +1 完整程序。
  • 它应该是一个程序,但如果有人自己发布这个功能也很好,因为它仍然增加了有趣的价值。

标签: code-golf


【解决方案1】:

LabVIEW 7.1

ONE 字符,即框图中的蓝色常量“1”。 我发誓,输入是复制和粘贴 ;-)

http://i25.tinypic.com/hvc4mp.png

http://i26.tinypic.com/5pnas.png

【讨论】:

  • hmmm - 保存时生成的 vi 到底有多大? ;)
  • 我猜。这很像“在我想象中的 X 语言中,它只有一个字符,因为这就是语言所做的一切!”键入答案。当然,没有“代码”……因为这一切都在底层系统的设计中。尽管如此,原创性还是值得称赞的。
  • 生成的 VI 的大小正好是 31.126 字节。
【解决方案2】:

Perl

perl 21 个字符,调用 31 个字符,总共 36 次击键(计算移位和最终返回):

perl -pe's/$1//gwhile/(.).*\1/'

【讨论】:

  • 习惯上贴出语言和字数。
  • (免责声明:我不是 Perl 大师。)我使用 v5.8.8 进行了测试,看来您可以删除 -pe 后的空格来保存字符。
  • 我们可以删除一些字符。你对那个空间是正确的。此外,while 后面的空格应该是多余的,分号也是多余的,因为它只有一行。但是,35 个角色没有很快就会被打败。
  • 该死,我不知道 Perl 会让你逃脱 s///gwhile。这有点荒谬。
  • perl -pe'y///cs' 可以工作,如果保证复制的字符彼此相邻。
【解决方案3】:

红宝石 — 61 53 51 56 35

61 个字符,统治者说。 (给了我另一个代码高尔夫的想法……)

  puts ((i=gets.split(''))-i.select{|c|i.to_s.count(c)<2}).join
+-------------------------------------------------------------------------+
||    |    |    |    |    |    |    |    |    |    |    |    |    |    |  |
|0         10        20        30        40        50        60        70 |
|                                                                         |
+-------------------------------------------------------------------------+
  gets.chars{|c|$><<c[$_.count(c)-1]}

... 35 由 Nakilon

【讨论】:

  • 使用较新版本的 ruby​​,您可以删除 '.to_s'。实际上,出于几个不同的原因,我更喜欢你的。
【解决方案4】:

APL

23 个字符:

(((1+ρx)-(ϕx)ιx)=xιx)/x

我是 APL 新手(昨天才知道),所以请善待 - 这当然不是最有效的方法。我很惭愧我没有击败 Perl。

再一次,当一个新手在 APL 中解决这个问题的最自然的方法仍然比迄今为止任何语言中的任何其他解决方案更简洁时,也许它说明了什么。

【讨论】:

    【解决方案5】:

    Python:

    s=raw_input()
    print filter(lambda c:s.count(c)<2,s)
    

    这是一个完整的工作程序,可以读取和写入控制台。单行版本可以直接从命令行使用

    python -c 's=raw_input();print filter(lambda c:s.count(c)<2,s)'
    

    【讨论】:

    • 这在我的 Python 版本中似乎不起作用,除非我在数组结构周围添加方括号 - join([...])。
    • 这是一个生成器表达式,是在 Python 2.2 中添加的。我认为可以合理地假设每个实际使用 Python 的人都在该版本或更高版本上。
    • 我使用的是 Python 2.3.4 (RHEL4),它抱怨上面的表达式 - “SyntaxError: invalid syntax”。可能只在 Python 2.4 及更高版本中?
    • 在 2.4 中添加了生成器表达式。反正新版本比较短,不使用了。
    • 你可以使用input()来减少字符。你能添加一个字符数吗?
    【解决方案6】:

    J(16 12 个字符)

    (~.{~[:I.1=#/.~)

    例子:

    (~.{~[:I.1=#/.~) 'nbHHkRvrXbvkn'
        RrX

    只需要括号默认执行即可。如果放在动词中,实际代码本身将是 14 个字符。

    当然有更聪明的方法来做到这一点。

    编辑:有问题的更聪明的方法:

    (~.#~1=#/.~) 'nbHHkRvrXbvkn'
        RrX

    12 个字符,如果设置在动词中则只有 10 个。我仍然讨厌它两次遍历列表的事实,一次是计数 (#/.),另一次是返回唯一值 (nub 或 ~.),但即使是 nubcount,'misc' 库中的标准动词也会执行两次。

    【讨论】:

    • 另一种使用 12 个字符的方法是 (]-.-.@~:#]),其优点是只需遍历列表一次。它只是从列表中复制 nubsieve 的 not,然后从列表中删除这些元素。
    • @David:还有更多表情符号。
    【解决方案7】:

    哈斯克尔

    在 Haskell 中肯定有更短的方法可以做到这一点,但是:

    Prelude Data.List> let h y=[x|x<-y,(<2).length$filter(==x)y]
    Prelude Data.List> h "nbHHkRvrXbvkn"
    "RrX"
    

    忽略 let,因为它只是 GHCi 中的函数声明所必需的,所以我们有 h y=[x|x&lt;-y,(&lt;2).length$filter(==x)y],它是 37 个字符(这与 "".join(c for c in s if s.count(c)&lt;2) 的当前“核心”Python 相关联,无论如何它实际上是相同的代码)。

    如果你想把它做成一个完整的程序,

    h y=[x|x<-y,(<2).length$filter(==x)y]
    main=interact h
    
    $ echo "nbHHkRvrXbvkn" | runghc tmp.hs
    RrX
    
    $ wc -c tmp.hs
    54 tmp.hs
    

    或者我们可以这样敲掉一个字符:

    main=interact(\y->[x|x<-y,(<2).length$filter(==x)y])
    
    $ echo "nbHHkRvrXbvkn" | runghc tmp2.hs
    RrX
    
    $ wc -c tmp2.hs
    53 tmp2.hs
    

    它在标准输入的所有上运行,而不是逐行运行,但这似乎可以接受 IMO。

    【讨论】:

    【解决方案8】:

    C89(106 个字符)

    这个使用了与我原来的答案完全不同的方法。有趣的是,写完再看another answer,我发现方法非常相似。感谢 caf 在我之前提出这种方法。

    b[256];l;x;main(c){while((c=getchar())>=0)b[c]=b[c]?1:--l;
    for(;x-->l;)for(c=256;c;)b[--c]-x?0:putchar(c);}
    

    一行是 58+48 = 106 字节

    C89(173 个字符)

    这是我最初的答案。正如cmets中所说,它的效果不太好......

    #include<stdio.h>
    main(l,s){char*b,*d;for(b=l=s=0;l==s;s+=fread(b+s,1,9,stdin))b=realloc(b,l+=9)
    ;d=b;for(l=0;l<s;++d)if(!memchr(b,*d,l)&!memchr(d+1,*d,s-l++-1))putchar(*d);}
    

    在两行中,它是 17+1+78+77 = 173 字节

    【讨论】:

    • 您正在调用 reallocmemchr,但范围内没有兼容的声明。
    • 什么语言?
    • @Svish - C. 也许你应该多出去走走。我不知道 Ruby 或 C#,但当我看到它们时,我或多或少地认出了它们。
    • @caf:不正确。在 NULL 上调用 realloc()(b 从 0 初始化)很好,并且行为类似于 malloc()。
    • @unwind - 他说他们没有被宣布,即没有extern void *realloc(void *ptr, size_t len);。当然,您真正需要的只是void *realloc();,但它们确实在技术上需要声明,因为它们不返回int
    【解决方案9】:

    C#

    65 个字符:

    new String(h.Where(x=>h.IndexOf(x)==h.LastIndexOf(x)).ToArray());
    

    67 个重新分配的字符:

    h=new String(h.Where(x=>h.IndexOf(x)==h.LastIndexOf(x)).ToArray());
    

    【讨论】:

      【解决方案10】:

      C#

      new string(input.GroupBy(c => c).Where(g => g.Count() == 1).ToArray());
      

      71 个字符

      【讨论】:

      • 发现使用您的方法实际上可以少几个字符,将 cs 更改为 c 并删除空格。然后它变成66个字符。虽然我找到了一个少一个字符的方法。
      • "input" 是字符串,所以它没有 GroupBy 方法。您应该添加对“ToCharArray”方法的调用。
      • System.String 实现了 IEnumerable,所以它有 GroupBy 方法。 IDE 对 string 上的扩展方法进行了例外处理,不显示它们,但它们编译和运行得很好。
      • Bryan,这只是 Visual Studio 的情况,因为我确实看到它们出现在 #D 中。
      • 很公平,有时您会忘记还有其他选择 :-)
      【解决方案11】:

      PHP(136 个字符)

      <?PHP
      function q($x){return $x<2;}echo implode(array_keys(array_filter(
      array_count_values(str_split(stream_get_contents(STDIN))),'q')));
      

      一行是 5+1+65+65 = 136 字节。使用 PHP 5.3,您可以节省几个字节,使函数匿名,但我现在无法测试。也许是这样的:

      <?PHP
      echo implode(array_keys(array_filter(array_count_values(str_split(
      stream_get_contents(STDIN))),function($x){return $x<2;})));
      

      即 5+1+66+59 = 131 个字节。

      【讨论】:

        【解决方案12】:

        另一种 APL 解决方案

        作为动态函数(18 个字符)

        {(1+=/¨(ω∘∊¨ω))/ω}
        

        假设输入在变量 x(16 个字符)中的行:

        (1+=/¨(x∘∊¨x))/x
        

        【讨论】:

          【解决方案13】:

          VB.NET

          For Each c In s : s = IIf(s.LastIndexOf(c) <> s.IndexOf(c), s.Replace(CStr(c), Nothing), s) : Next
          

          当然,VB 不是尝试保存字符的最佳语言,但行出来的字符数为 98 个。

          【讨论】:

          • 我不是 VB 专家,但您的答案中似乎有一堆空白,vb 是否需要所有这些空白?
          • 如果它是 VB6 之类的东西,那么它会强制您使用空格。
          • 是的,IDE 会为您添加空格。
          • 不,大多数空格在这里绝对没用。作为高级 IDE,VS 会为您插入它们。但在代码高尔夫中,它们不应该被计算在内。
          • 是的,即使 VS 自动插入空格,它也可以在没有空格的情况下工作。使用If 而不是IIf 会更好。您也可以跳过将 c 转换为字符串。为什么不替换为"" 而不是Nothing...所有这些都将导致这个75 字符代码:For Each c In s:s=If(s.LastIndexOf(c)&lt;&gt;s.IndexOf(c),s.Replace(c,""),s):Next
          【解决方案14】:

          PowerShell

          61 个字符。其中$s="nbHHkRvrXbvkn"$a 是结果。

          $h=@{}
          ($c=[char[]]$s)|%{$h[$_]++}
          $c|%{if($h[$_]-eq1){$a+=$_}}
          

          功能齐全的参数化脚本:

          param($s)
          $h=@{}
          ($c=[char[]]$s)|%{$h[$_]++}
          $c|%{if($h[$_]-eq1){$a+=$_}}
          $a
          

          【讨论】:

          • 我设法将它提高到 58。我不敢相信我只花了 20 分钟。 [字符[]]$s|%{if($s -clike"*$_*$_*"){$s=$s -creplace$_,''}}
          【解决方案15】:

          C: 83 89 93 99 101 个字符

          • O(n2) 时间。
          • 限制为 999 个字符。
          • 仅在 32 位模式下工作(由于不是 #include-ing &lt;stdio.h&gt;(花费 18 个字符)使 gets 的返回类型被解释为 int 并砍掉一半的地址位)。
          • 显示友好的“警告:此程序使用gets(),这是不安全的”。在 Mac 上。

          .

          main(){char s[999],*c=gets(s);for(;*c;c++)strchr(s,*c)-strrchr(s,*c)||putchar(*c);}
          

          (这个类似的 82-chars 版本通过命令行输入:

          main(char*c,char**S){for(c=*++S;*c;c++)strchr(*S,*c)-strrchr(*S,*c)||putchar(*c);}
          

          )

          【讨论】:

            【解决方案16】:

            Golfscript(sym) - 15

              .`{\{=}+,,(!}+,
            +-------------------------------------------------------------------------+
            ||    |    |    |    |    |    |    |    |    |    |    |    |    |    |  |
            |0         10        20        30        40        50        60        70 |
            |                                                                         |
            +-------------------------------------------------------------------------+
            

            【讨论】:

            • 我从红宝石答案中捏出来的 :)
            【解决方案17】:

            哈斯克尔

            (只是从 Mark Rushakoff 的努力中删除了几个字符,我宁愿将它作为对他的评论发布)

            h y=[x|x<-y,[_]<-[filter(==x)y]]
            

            这是更好的 Haskell 习惯用法,但对于非 Haskell 的人来说可能比这更难理解:

            h y=[z|x<-y,[z]<-[filter(==x)y]]
            

            编辑为hiena和其他人添加解释:

            我假设您理解 Mark 的版本,所以我将仅介绍更改。马克的表情:

            (<2).length $ filter (==x) y
            

            过滤y 以获取== x 的元素列表,找到该列表的长度并确保它小于两个。 (实际上它必须是长度一,但==1&lt;2 长)我的版本:

            [z] <- [filter(==x)y]
            

            执行相同的过滤,然后将结果列表放入作为唯一元素的列表。现在箭头(看起来像集合包含!)表示“对于 RHS 列表的每个元素,依次调用该元素 [z]”。 [z] 是包含单个元素z 的列表,因此元素“filter(==x)y”只有在包含一个元素时才能称为“[z]”。否则它将被丢弃并且永远不会用作z 的值。所以z(在列表理解中| 的左侧返回)正是使filter 返回长度为1 的列表的x

            那是我的第二个版本,我的第一个版本返回 x 而不是 z - 因为它们无论如何都是一样的 - 并将 z 重命名为 _ 这是“这个值不是”的 Haskell 符号t 将被使用,所以我不会通过给它命名来使我的代码复杂化”。

            【讨论】:

            • 您能解释一下您的解决方案吗?我无法理解:S
            • 对于那些感兴趣的人,我已经发布了一个无点风格的 haskell 解决方案:stackoverflow.com/questions/1344352/…
            【解决方案18】:

            Javascript 1.8

            s.split('').filter(function (o,i,a) a.filter(function(p) o===p).length <2 ).join('');
            

            或替代-类似于python示例:

            [s[c] for (c in s) if (s.split("").filter(function(p) s[c]===p).length <2)].join('');
            

            【讨论】:

            • 为什么要保留所有无关紧要的空白字符?
            • @KennyTM 没有意识到我可以删除它们?
            • 喜欢[s[c]for(c in s)if(s.split("").filter(function(p)s[c]===p).length&lt;2)].join(''); 所以长度减少到79。
            【解决方案19】:

            TCL

            123 个字符。或许可以缩短它,但这对我来说已经足够了。

            proc h {i {r {}}} {foreach c [split $i {}] {if {[llength [split $i $c]]==2} {set r $r$c}}
            return $r}
            puts [h [gets stdin]]
            

            【讨论】:

              【解决方案20】:

              C

              完整的 C 语言程序,141 字节(包括换行符)。

              #include<stdio.h>
              c,n[256],o,i=1;main(){for(;c-EOF;c=getchar())c-EOF?n[c]=n[c]?-1:o++:0;for(;i<o;i++)for(c=0;c<256;c++)n[c]-i?0:putchar(c);}
              

              【讨论】:

              • 你可以在 ANSI C 中声明没有类型的变量了吗?
              • 没关系。显然你可以,尽管 GCC 会发出警告,即使所有警告都关闭了。
              • 非常好!我现在必须优化我自己的方法来击败你的方法。 =]
              • 我自己设法降到了 136 字节,碰巧使用了和你类似的方法。 (它可能是相同的,甚至......)
              【解决方案21】:

              斯卡拉

              54 个字符仅用于方法主体,66 个带有(静态类型)方法声明:

              def s(s:String)=(""/:s)((a,b)=>if(s.filter(c=>c==b).size>1)a else a+b)
              

              【讨论】:

                【解决方案22】:

                红宝石

                63 个字符。

                puts (t=gets.split(//)).map{|i|t.count(i)>1?nil:i}.compact.join
                

                【讨论】:

                【解决方案23】:

                VB.NET / LINQ

                96 个字符用于完整的工作声明

                Dim p=New String((From c In"nbHHkRvrXbvkn"Group c By c Into i=Count Where i=1 Select c).ToArray)

                完整的工作语句,带有原始字符串和 VB 特定的“漂亮列表(代码重新格式化”)关闭,96 个字符,非工作语句没有原始字符串,84 个字符。

                (请确保您的代码在回答之前有效。谢谢。)

                【讨论】:

                  【解决方案24】:

                  C

                  (第一版:112 个字符;第二版:107 个字符)

                  k[256],o[100000],p,c;main(){while((c=getchar())!=-1)++k[o[p++]=c];for(c=0;c<p;c++)if(k[o[c]]==1)putchar(o[c]);}
                  

                  那是

                  /* #include <stdio.h> */
                  /* int */ k[256], o[100000], p, c;
                  /* int */ main(/* void */) {
                    while((c=getchar()) != -1/*EOF*/) {
                      ++k[o[p++] = /*(unsigned char)*/c];
                    }
                    for(c=0; c<p; c++) {
                      if(k[o[c]] == 1) {
                        putchar(o[c]);
                      }
                    }
                    /* return 0; */
                  }
                  

                  因为 getchar() 返回 int 而 putchar 接受 int,所以可以“安全地”删除 #include。 没有包含,EOF 没有定义,所以我使用 -1 代替(并获得了一个字符)。 该程序仅适用于少于 100000 个字符的输入!

                  版本 2,感谢 strager 107 个字符

                  #ifdef NICE_LAYOUT
                  #include <stdio.h>
                  
                  /* global variables are initialized to 0 */
                  int char_count[256];                          /* k in the other layout */
                  int char_order[999999];                       /* o ... */
                  int char_index;                               /* p  */
                  
                  int main(int ch_n_loop, char **dummy)         /* c  */
                                                                /* variable with 2 uses */
                  {
                  
                    (void)dummy; /* make warning about unused variable go away */
                  
                    while ((ch_n_loop = getchar()) >= 0) /* EOF is, by definition, negative */
                    {
                      ++char_count[ ( char_order[char_index++] = ch_n_loop ) ];
                      /* assignment, and increment, inside the array index */
                    }
                    /* reuse ch_n_loop */
                    for (ch_n_loop = 0; ch_n_loop < char_index; ch_n_loop++) {
                      (char_count[char_order[ch_n_loop]] - 1) ? 0 : putchar(char_order[ch_n_loop]);
                    }
                    return 0;
                  }
                  #else
                  k[256],o[999999],p;main(c){while((c=getchar())>=0)++k[o[p++]=c];for(c=0;c<p;c++)k[o[c]]-1?0:putchar(o[c]);}
                  #endif
                  

                  【讨论】:

                  • 一些建议:您可以通过将 c 作为参数传递给 main 来节省一个字节。您也可以使用 k[o[c]]-1?0:putchar(o[c]);在您的第二个循环中也可以节省一些字节。您可以为您的大数组使用 9 而不是 0 来保存字节(在源大小和内存中)。
                  【解决方案25】:

                  Javascript 1.6

                  s.match(/(.)(?=.*\1)/g).map(function(m){s=s.replace(RegExp(m,'g'),'')})
                  

                  比之前发布的 Javascript 1.8 解决方案更短(71 个字符对 85 个)

                  【讨论】:

                  • 感谢@KennyTM 的改进
                  【解决方案26】:

                  汇编器

                  用 WinXP DOS 盒子 (cmd.exe) 测试:

                      xchg cx,bp
                      std
                      mov al,2
                      rep stosb
                      inc cl
                  l0: ; to save a byte, I've encoded the instruction to exit the program into the
                      ; low byte of the offset in the following instruction:
                      lea si,[di+01c3h] 
                      push si
                  l1: mov dx,bp
                      mov ah,6
                      int 21h
                      jz l2
                      mov bl,al
                      shr byte ptr [di+bx],cl
                      jz l1
                      inc si
                      mov [si],bx
                      jmp l1
                  l2: pop si
                  l3: inc si
                      mov bl,[si]
                      cmp bl,bh
                      je l0+2
                      cmp [di+bx],cl
                      jne l3
                      mov dl,bl
                      mov ah,2
                      int 21h
                      jmp l3
                  

                  汇编为 53 个字节。读取标准输入并将结果写入标准输出,例如:

                   programname < input > output
                  

                  【讨论】:

                    【解决方案27】:

                    PHP

                    118 个字符的实际代码(加上 PHP 块标记的 6 个字符):

                    <?php
                    $s=trim(fgets(STDIN));$x='';while(strlen($s)){$t=str_replace($s[0],'',substr($s,1),$c);$x.=$c?'':$s[0];$s=$t;}echo$x;
                    

                    【讨论】:

                      【解决方案28】:

                      C#(53 个字符)

                      其中 s 是您的输入字符串:

                      new string(s.Where(c=>s.Count(h=>h==c)<2).ToArray());
                      

                      或 59 重新分配:

                      var a=new string(s.Where(c=>s.Count(h=>h==c)<2).ToArray());
                      

                      【讨论】:

                        【解决方案29】:

                        Haskell Pointfree

                        import Data.List
                        import Control.Monad
                        import Control.Arrow
                        main=interact$liftM2(\\)nub$ap(\\)nub
                        

                        整个程序是97个字符,但真正的肉只有23个字符。剩下的只是导入并将函数带入 IO monad。在加载了模块的 ghci 中,它只是

                        (liftM2(\\)nub$ap(\\)nub) "nbHHkRvrXbvkn"
                        

                        更荒谬的无点风格(无意义风格?):

                        main=interact$liftM2 ap liftM2 ap(\\)nub
                        

                        虽然函数本身有 26 个字符,但它有点长。

                        【讨论】:

                          【解决方案30】:

                          Shell/Coreutils,37 个字符

                          fold -w1|sort|uniq -u|paste -s -d ''
                          

                          【讨论】:

                            猜你喜欢
                            • 2013-11-12
                            • 1970-01-01
                            • 2020-03-30
                            • 1970-01-01
                            • 2012-04-08
                            • 1970-01-01
                            • 1970-01-01
                            • 2018-02-20
                            相关资源
                            最近更新 更多