Detriplicare una stringa


39

Molte lingue hanno modi integrati per sbarazzarsi dei duplicati o "deduplicare" o "unificare" un elenco o una stringa. Un'attività meno comune è "detriplicare" una stringa. Cioè, per ogni personaggio che appare, vengono mantenute le prime due occorrenze.

Ecco un esempio in cui i caratteri che devono essere eliminati sono etichettati con ^:

aaabcbccdbabdcd
  ^    ^ ^^^ ^^
aabcbcdd

Il tuo compito è implementare esattamente questa operazione.

Regole

L'input è una stringa singola, possibilmente vuota. Si può presumere che contenga solo lettere minuscole nell'intervallo ASCII.

L'output dovrebbe essere una singola stringa con tutti i caratteri rimossi che sono già apparsi almeno due volte nella stringa (quindi vengono mantenute le due occorrenze più a sinistra).

Invece di stringhe puoi lavorare con liste di caratteri (o stringhe singleton), ma il formato deve essere coerente tra input e output.

È possibile scrivere un programma o una funzione e utilizzare uno qualsiasi dei nostri metodi standard per ricevere input e fornire output.

È possibile utilizzare qualsiasi linguaggio di programmazione , ma si noti che queste scappatoie sono vietate per impostazione predefinita.

Questo è , quindi la risposta valida più breve - misurata in byte - vince.

Casi test

Ogni coppia di righe è un caso di test, input seguito da output.



xxxxx
xx
abcabc
abcabc
abcdabcaba
abcdabc
abacbadcba
abacbdc
aaabcbccdbabdcd
aabcbcdd

Classifica

Lo snippet di stack nella parte inferiore di questo post genera una classifica dalle risposte a) come elenco della soluzione più breve per lingua eb) come classifica generale.

Per assicurarti che la tua risposta venga visualizzata, ti preghiamo di iniziare la risposta con un titolo, usando il seguente modello Markdown:

## Language Name, N bytes

dov'è Nla dimensione del tuo invio. Se si migliora il punteggio, è possibile mantenere i vecchi punteggi nel titolo, colpendoli. Per esempio:

## Ruby, <s>104</s> <s>101</s> 96 bytes

Se si desidera includere più numeri nell'intestazione (ad es. Perché il punteggio è la somma di due file o si desidera elencare separatamente le penalità del flag dell'interprete), assicurarsi che il punteggio effettivo sia l' ultimo numero nell'intestazione:

## Perl, 43 + 3 (-p flag) = 45 bytes

Puoi anche rendere il nome della lingua un collegamento che verrà quindi visualizzato nello snippet:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes


5
Stringhe singleton ... stringletons?
dkudriavtsev,

Risposte:



15

JavaScript (ES6), 42 48

Modifica Un enorme 6 byte salvato thx @Neil

s=>s.replace(k=/./g,c=>(k[c]+=c)[11]?'':c)

Spiegazione: Uso le proprietà 'a' ... 'z' dell'oggetto kper memorizzare le informazioni per ciascun personaggio (in questo caso l'oggetto k è una regexp solo per salvare byte). Queste proprietà sono inizialmente undefined. In javascript l'aggiunta di un numero a undefinedNaN(abbastanza ragionevole), ma l'aggiunta di una stringa 'X' dà "undefinedX"- una stringa di lunghezza 10 (sciocca). Aggiungendo più caratteri si ottengono stringhe più lunghe. Se la stringa ottenuta per un dato carattere è più lunga di 11, quel carattere non viene copiato nell'output.

Test

F=
s=>s.replace(k=/./g,c=>(k[c]+=c)[11]?'':c)

test=`

xxxxx
xx
abcabc
abcabc
abcdabcaba
abcdabc
abacbadcba
abacbdc
aaabcbccdbabdcd
aabcbcdd`.split`\n`
for(i=0;i<test.length;)
  a=test[i++],b=test[i++],r=F(a),
  console.log(r==b?'OK':'KO',a,'->',r,b)


A rigor di termini una riga vuota è uno dei casi di test.
Neil,

@Neil ok ha aggiunto il test delle stringhe vuote
edc65,

Se si passa all'input e all'output dell'array, è possibile utilizzare .filter per salvare altri 12 caratteri. v=>v.filter(x=>!(v[x]+=x)[11]). Complimenti per l'hack "non definito".
Grax32

@Grax grazie ma troppo diverso. Dovresti
postarlo

14

Python 2, 48 byte

lambda s:reduce(lambda r,c:r+c*(r.count(c)<2),s)

c[r.count(c)/2:]è un'alternativa della stessa lunghezza a c*(r.count(c)<2).


49 byte:

r=''
for c in input():r+=c*(r.count(c)<2)
print r

12

Retina , 17 byte

(.)(?<=\1.*\1.+)

Provalo online!

Sostituzione regex semplice: abbina un personaggio se è già apparso due volte e rimuovilo.


Ho anche provato un ciclo e un gruppo ripetuto con{2} , entrambi con 18 byte.
Kobi,

1
Ne ho 14 usando una funzione aggiunta di recente. ;)
Martin Ender il

Sapevo che c'era qualcosa. Ho osservato i limiti, probabilmente non quello. Controllerò di nuovo.
Kobi,

3
Ah, penso di aver trovato la risposta di Martin. Ho avuto qualche problema quando stavo provando prima, penso perché non ho considerato come il deduplicato avrebbe funzionato su un input multilinea. Spoiler (con 5 byte aggiunti per abilitare la modalità per linea): retina.tryitonline.net/…
FryAmTheEggman

@FryAmTheEggman - Bello, non ho trovato questo. Sentiti libero di aggiungere una risposta - Penso che sia troppo diverso dalla mia risposta e non mi sentivo a mio agio nel modificarla :P. Grazie!
Kobi,

6

Brachylog , 25 byte

.v|s.g:.z:1a
:2fl<3
he~t?

Provalo online! o verifica tutti i casi di test .

Spiegazione

Questo funziona perché prima s - Subsetsi unificheranno con i sottoinsiemi più grandi, quindi ad esempio perché "aaa"tenterà "aa"prima "a".

  • Predicato principale:

      .v         input = Output = ""
    |          Or
      s.         Output is an ordered subset of the input
      g:.z       Zip each character of the output with the output itself
      :1a        Apply predicate 1 on each element of the zip
    
  • Predicato 1: controlla che tutti i personaggi compaiano al massimo due volte. Input =[String:Char]

    :2f        Find all valid outputs of predicate 2 (i.e. one output per occurence
                   of the char)
    l<3        There are less than 3 occurences
    
  • Predicato 2: Ottieni l'occorrenza di un personaggio. Input =[String:Char]

    he         Take a character of the string in the input
      ~t?      That character is the char of the input
    

6

> <> , 22 byte

i:0(?;::9g:}2(?o{1+$9p

Provalo online! Utilizza la codebox per tenere traccia dei conteggi finora.

i                       Read a char c of input
 :0(?;                  Halt if EOF
      :                 Make a copy - stack has [c c] at the top
       :9g              Get count stored at (c, 9)
          :}            Copy the count and move to bottom of stack
            2(?o        If the count is less than 2, output c
                {1+     Move the count back to the top of the stack and increment
                   $9p  Update cell at (c, 9)
                        [Instruction pointer moves to start as ><> is toroidal]

6

J, 20 15 byte

#~(3>[+/@:={:)\

Questo definisce una funzione monadica che accetta e restituisce una stringa. Provalo qui . Uso:

   f =: #~(3>[+/@:={:)\
   f 'abaacbb'
abacb

Spiegazione

Sono passato allo stesso algoritmo utilizzato da alcune altre soluzioni, poiché si è rivelato più breve ...

#~(3>[+/@:={:)\  Input is y.
  (          )\  For each prefix of y:
          =        compute the equality vector
     [     {:      of the prefix and its last element, and
      +/@:         take its sum. Now we have a vector r such that y[i] has its
                   r[i]'th occurrence at position i.
   3>              Mark those coordinates where r[i] < 3.
#~               Remove the non-marked characters from y.

6

Haskell, 40 39 byte

foldl(\s c->s++[c|filter(==c)s<=[c]])""

Esempio di utilizzo: foldl(\s c->s++[c|filter(==c)s<=[c]])"" "aaabcbccdbabdcd"-> "aabcbcdd".

Mantieni il carattere successivo cse la stringa di tutti i cs finora è lessicografica minore o uguale alla stringa di singleton [c].

Modifica: @xnor ha salvato un byte passando dalla comprensione dell'elenco a filter. Grazie!


La tua alternativa potrebbe fare filter(==c)s<=[c]per salvare un byte.
xnor

5

Perl, 22 byte

21 byte codice + 1 per -p.

s/./$&x(2>${$&}++)/ge

uso

perl -pe 's/./$&x(2>${$&}++)/ge' <<< 'aaabcbccdbabdcd'
aabcbcdd

5

C, 57 byte

Chiama f()con la stringa per detriplicare. La funzione modifica il suo parametro. Richiede C99 a causa della fordichiarazione -loop.

f(char*p){for(char*s=p,m[256]={0};*s=*p;s+=++m[*p++]<3);}

Non puoi inserire la dichiarazione snella prima dichiarazione del for?
Martin Ender,

In C99 puoi. Non l'ho fatto perché mi piace mantenere il golf C89 compatibile.
owacoder,

5

JavaScript (ES6), 35 byte

s=>s.filter(c=>(s[c]=(s[c]|0)+1)<3)

Accetta una matrice di caratteri come input e restituisce la matrice detriplicata.


Bello. Potresti fare c=>(s[c]=-~s[c])<3per salvare qualche byte.
ETHproductions

Mi mancava che potessi usare le matrici come input e avevo scritto una funzione usando map. Golfato sembrava essenzialmente come il tuo. la differenza principale era il compito, che se lo cambierà salverà alcuni byte. Prova s.filter(c=>(s[c]=s[c]+1|0)<3)per 33 byte. EDIT: Ops, perse il commento sopra di me, che è ancora meglio :)
Gen

4

PowerShell v2 +, 31 byte

$args-replace'(.)(?<=\1.*\1.+)'

Utilizza la stessa regex della risposta Retina di Kobi , incapsulata nell'operatore PowerShell -replace. Funziona perché entrambi utilizzano regex di sapore .NET in background.

In alternativa, senza regex, 56 byte

$b=,0*200;-join([char[]]$args[0]|%{"$_"*($b[$_]++-lt2)})

Crea un array di helper $bprecompilato con 0s. Lancia la stringa di input $args[0]come un chararray, convogliandola attraverso un loop |%{...}. Ogni iterazione genera il carattere corrente $_come una stringa "$_"moltiplicata per un valore booleano che è $TRUE(implicitamente cast 1qui) se il punto appropriato nell'array helper è inferiore a 2(cioè, non abbiamo già visto questo carattere due volte). La risultante raccolta di stringhe è incapsulata in parentesi ed edita -joininsieme per formare una singola stringa di output. Rimane in cantiere e l'output è implicito.


regex è imbattibile. :) Ho beleave una tabella hash è meglio quindi una matrice per la variante senza regex: $b=@{};-join($args|% t*y|?{++$b.$_-lt3}).
mazzy,

1
@mazzy Per la variante senza regex e il tuo codice, dovrebbe essere una versione più recente di PowerShell 2. Di conseguenza, penso che terrò questa risposta senza modifiche. Puoi pubblicare il tuo codice come risposta separata, però!
AdmBorkBork,

l'hashtable è apparso nella versione 3.0? Ok. Grazie.
mazzy,

4

Mathematica, 39 byte

Fold[If[Count@##<2,Append@##,#]&,{},#]&

Funzione anonima. Accetta un elenco di caratteri come input e restituisce l'elenco detriplicato come output. Utilizza il metodo di ripiegare l'elenco e rifiutare gli elementi triplicati, non è troppo complicato.


4

05AB1E, 12 byte

vyˆ¯y¢O3‹iy?

Spiegazione

v            # for each char in input
 yˆ          # push to global array
   ¯y¢O3‹i   # if nr of occurrences are less than 3
          y? # print it

Provalo online


4

MATL , 8 byte

t&=Rs3<)

Provalo online!

Spiegazione

t      % Input string implicitly. Push another copy
&=     % Matrix of all pairwise equality comparisons of string elements
R      % Keep only upper triangular part, making the rest of the entries zero
s      % Sum of each column. This gives a vector with number of occurrences
       % of the current character up to the current position
3<     % True for entries that are less than 3
)      % Use as logical index into initial copy of the input. Display implicitly

Esempio

Supponendo che l'input 'aaababbc', lo stack contenga quanto segue dopo le istruzioni indicate:

  • t

    'aaababbc'
    'aaababbc'
    
  • t&=

    'aaababbc'
    [ 1 1 1 0 1 0 0 0;
      1 1 1 0 1 0 0 0;
      1 1 1 0 1 0 0 0;
      0 0 0 1 0 1 1 0;
      1 1 1 0 1 0 0 0;
      0 0 0 1 0 1 1 0;
      0 0 0 1 0 1 1 0;
      0 0 0 0 0 0 0 1 ]
    
  • t&=R

    'aaababbc'
    [ 1 1 1 0 1 0 0 0;
      0 1 1 0 1 0 0 0;
      0 0 1 0 1 0 0 0;
      0 0 0 1 0 1 1 0;
      0 0 0 0 1 0 0 0;
      0 0 0 0 0 1 1 0;
      0 0 0 0 0 0 1 0;
      0 0 0 0 0 0 0 1 ]
    
  • t&=Rs

    'aaababbc'
    [ 1 2 3 1 4 2 3 1 ]
    
  • t&=Rs3<

    'aaababbc'
    [ true true false true false true false true ]
    
  • t&=Rs3<)

    'aabbc'
    

4

Retina , 14 byte

D`(.)(?<=\1.*)

Verifica tutti i casi di test. ( %Abilita la modalità per linea)

Utilizza il nuovo stage "Deduplicate" per salvare un paio di byte sull'approccio di Kobi . Deduplicate raccoglie un elenco di tutte le corrispondenze nella regex e sostituisce tutte tranne la prima con la stringa vuota. La regex corrisponde a un carattere che appare già una volta nella stringa, il che significa che i primi due verranno mantenuti.



3

K, 18 byte

  g:{x{?x@<x}@,/2#'=x}
  g "abc"
"abc"
  g "aaabcbccdbabdcd"
"aabcbcdd"

  /k4 request test vectors from internet
  R:"GET /raw/ftHe0bpE HTTP/1.0\r\nHost: pastebin.com\r\n\r\n"
  t:+0N 2#t@1_&|\(0=#:)'t:1_"\r\n"\:`:http://pastebin.com:80 R 

  /k4 no internet? use a file called "t.txt" in current directory
  t:+0N 2#0:`:t.txt

  /k6?
  t:+0N 2#0:"t.txt"

  /visually inspect test cases
  g't[0]
(();"xx";"abcabc";"abcdabc";"abacbdc";"aabcbcdd")

  /do all tests pass?
  |/ t[1] {$[0=#x;0=#y;x~y]}' g't[0]
1b

K4 è disponibile per il download gratuito ; K6 è in fase di sviluppo . Se hai scaricato KDB, puoi entrare in K con la barra rovesciata .

Potrebbe essere più semplice vederlo diviso, ma prima una sintassi: g:ximposta gsu x. {x+1}è una funzione che accetta un argomento x . In K il primo argomento di una funzione è x(il secondo è ye il terzo èz . Non è necessario un quarto).

Adesso:

x:"aaabcbccdbabdcd"

=xsignifica gruppo x , che produce:

"abcd"!(0 1 2 10;3 5 9 11;4 6 7 13;8 12 14)

2#'significa due presi (da) ciascuno che produce

"abcd"!(0 1;3 5;4 6;8 12)

Come puoi vedere, questi sono gli offset delle prime due partite di ciascun personaggio. Il 2 potrebbe essere generalizzato.

,/significa unirsi a ciascuno e viene spesso chiamato raze . Ci porterà solo i valori del nostro dizionario. Pertanto, ,/"abcd"!(0 1;3 5;4 6;8 12)produce:

0 1 3 5 4 6 8 12

che dobbiamo ordinare. {x@<x}@è un linguaggio che i programmatori K vedono spesso (Q lo chiama asc ), che dice x al grado superiore x . Rompendolo:

  <0 1 3 5 4 6 8 12
0 1 2 4 3 5 6 7

ha restituito gli indici dell'array ordinato, che vogliamo prendere dall'array originale. x@ysignifica x at y, quindi indicizza l'array con gli indici dell'ordinamento (se questo ha senso).

  {x@<x}@0 1 3 5 4 6 8 12
0 1 3 4 5 6 8 12

che ora semplicemente indicizziamo nel nostro array originale. Noi potremmo dire x@qui, ma K supporta un concetto molto potente, che siamo in grado di trarre vantaggio da qui: applicazione funzione è l'indicizzazione. Ciò significa che a[0]potrebbe cercare lo slot zeroth ao potrebbe applicare 0la funzione chiamata a. Il motivo per cui avevamo bisogno del @precedente {x@<x}è perché x<ysignifica xs inferiore a ys : gli operatori in K hanno una forma diadica (a due argomenti) e una forma monadica (a un argomento) che proviene da APL. Q non ha questa "ambivalenza".


Benvenuti in PPCG! Ottima prima risposta. :)
Martin Ender,

Ho un paio di domande. 1. K4 è la stessa lingua a cui ti colleghi (Q / kdb +)? 2. Potresti mostrare come chiamare la tua funzione su un input o come devono essere formattati gli elementi in testVectors.txt?
Dennis

@Dennis 1. Sì. Premi la barra rovesciata per passare da Q a K. 2. Proprio come appaiono nella domanda: pastebin.com/ftHe0bpE chiamata di esempio:g"aaabcbccdbabdcd"
geocar

Ok grazie. Impossibile far funzionare la parte del file, ma g"..."fa il trucco. Sfortunatamente, il tuo codice ritorna aabbccper l'input abc.
Dennis

@Dennis Potresti aver fatto qualcosa di sbagliato: {x{?x@<x}@,/2#'=x}"abc"sicuramente ritorna "abc". Ritornerebbe "aabbcc"se ti perdessi il ?distinto.
geocar


2

Java 8 lambda, 90 caratteri

i->{int[]o=new int[128];String r="";for(char c:i.toCharArray())if(++o[c]<3)r+=c;return r;}

Versione non golfata:

public class Q86503 {

    static String detriplicate(String input) {
        int[] occurences = new int[128];
        String result = "";
        for (char c : input.toCharArray()) {
            if (++occurences[c] < 3) {
                result += c;
            }
        }
        return result;
    }
}

Crea un array per tutti i caratteri ASCII. Se compare un personaggio, il contatore corrispondente verrà aumentato. Se è superiore a 2, il carattere non verrà aggiunto alla stringa del risultato. Molto facile, molto corto;)


2

Perl 6, 27 byte

{.comb.grep({++%.{$_} <3})}

Spiegazione:

{.comb.grep({++%.{$_} <3})}
{                         } # a function
 .comb                      # get all the characters in the argument
      .grep({           })  # filter
               %.           # an anonymous hash (shared between calls to grep)
             ++  {$_}       # increment the value at the current key (current letter).
                            # if the key doesn't exist, it defaults to 0 (then gets incremented)
                      <3    # return True if it wasn't seen 3 times

(Nota: Perl 6 non è "orientato al golf" come sua sorella Perl 5 ... Quindi sì, quello spazio prima del <necessario. %.{}È un hash anonimo).



2

SmileBASIC, 77 72 69 68 byte

DIM R[#Y]READ S$WHILE""<S$Q=ASC(S$)INC R[Q]?SHIFT(S$)*(R[Q]<3);
WEND

Ha spiegato:

DIM R[128] 'array to store letter frequencies
READ S$ 'get input string
WHILE""<S$ 'much shorter than LEN(S$)
 Q=ASC(S$) 'get ascii value of first character in S$
 INC R[Q]
 ?SHIFT(S$)*(R[Q]<3); 'remove the first character of S$, and print it if there are less than 3 occurrences.
WEND

Benvenuto in ppcg! Bel primo post!
Rɪᴋᴇʀ

1

Lisp comune, 127

(lambda(s)(map()(lambda(x)(flet((p(b)(1+(position x s :start b))))(setf s(remove x s :start(p(p 0))))))(remove-duplicates s))s)

Pretty-stampati

(lambda (s)
  (map nil
       (lambda (x)
         (flet ((p (b)
                  (1+ (position x s :start b))))
           (setf s (remove x s :start (p (p 0))))))
       (remove-duplicates s))
  s)

1

Q , 52 byte

q)f2:{x asc raze{distinct 2#where x}each x~'/:distinct x}
q)f2 each testList
"xx"
"abcabc"
"abcdabc"
"abacbdc"
"aabcbcdd"
q)

1

K , 27 byte

    f:{x{x@<x}@,/{?2#&x}'x~'/:?x}
    testList:("xxxxx";"abcabc";"abcdabcaba";"abacbadcba";"aaabcbccdbabdcd")
    f'testList
("xx";"abcabc";"abcdabc";"abacbdc";"aabcbcdd")

1

Rubino , 79 62 57 byte

Questo è piuttosto ingombrante, ma non sono sicuro di poter giocare a golf molto meglio al momento. Tutti i suggerimenti per il golf sono i benvenuti. Provalo online!

Modifica: -17 byte grazie a Value Ink, suggerendo un modo più golfista per rimuovere i personaggi triplicati. -5 byte dalla rimozione del .uniqmetodo.

->s{s.chars.map{|a|s[s.rindex a]=""while s.count(a)>2};s}

Ungolfed:

def g(s)
 s.chars.each do |a|
  while s.count(a) > 2
   i = s.rindex(a)
   s[i] = ""
  end
 end
 return s
end

62 byte:->s{s.chars.uniq.map{|a|s[s.rindex a]=""while s.count(a)>2};s}
Valore inchiostro

1

JavaScript, 30 byte

v=>v.filter(x=>!(v[x]+=x)[11])

Utilizzando il metodo fornito da @ edc65 per il conteggio ma con un filtro array. La prima volta che appare il carattere, il valore dell'oggetto diventa "non definito" più il carattere (cioè "undefinedx"). La volta successiva il valore dell'oggetto diventa "undefinedxx".

Successivamente, v [x] [11] restituisce true e, se combinato con l'operatore not, false, significa che verranno filtrati i caratteri che sono già comparsi due volte.


0

Javascript (utilizzando una libreria esterna) (80 byte)

Questa è stata una buona idea! Non ho vinto ma è stato divertente

n=>{a={};return _.From(n).Where(x=>{b=a[x]?a[x]++:a[x]=1;return b<2}).Write("")}

Link alla lib: https://github.com/mvegh1/Enumerable/

Spiegazione del codice: il metodo accetta una stringa, la libreria la analizza come un array di caratteri e la clausola Where è un predicato di filtro complesso che controlla l'hashmap 'a' per la presenza del carattere corrente. Se esiste, incrementa il contatore, altrimenti impostato su 1. Se <2, passa il predicato (e il carattere corrente), altrimenti fallisce

inserisci qui la descrizione dell'immagine


È possibile evitare di utilizzare un returnma fare la vostra funzione di un elenco separato da virgole di un espressioni tra parentesi: n=>(a={},_From(n)....). L'ultima espressione è il valore restituito. Nella vostra Wherefunzione, è possibile eliminare l'intermedio bdel tutto confrontando contro il risultato della cessione o minimo: x=>(a[x]?a[x]++:a[x]=1)<2.
apsillers,

Infine, è possibile evitare di utilizzare una libreria esterna a tutti (e salvare byte) utilizzando la stringa di puntini di sospensione-split e filtercon join: [...n].filter(...).join(""). Capovolgi la logica vera / falsa quando si cambia Wherein filter.
apsillers,

Ahh buone osservazioni! Darò un'occhiata più da vicino al tuo suggerimento
applejacks01

0

Clojure, 72 byte

#(apply str(reduce(fn[r c](if(<(count(filter #{c}r))2)(conj r c)r))[]%))

Così tanti byte ...


0

Pascal (FPC) , 103 byte

var a:array['a'..'z']of word;c:char;begin repeat read(c);inc(a[c]);if a[c]<3then write(c)until eof end.

Provalo online!

Spiegazione:

var a:array['a'..'z']of word; //used for counting occurences of characters in the input
                              //array indices are accessed by chars
    c:char;
begin
  repeat
    read(c);                  //read a character from input
    inc(a[c]);                //increment the count of that character (its number in array)
    if a[c]<3 then write(c)   //if this is character's 1st or 2nd occurence, output it
  until eof                   //go back to reading if input is not read completely
end.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.