Encontre colunas onde todos os caracteres são iguais


18

Me deparei com esta pergunta no SO e pensei que seria um bom desafio para o golfe. Então aqui está:

Desafio:

Escreva um programa que leia uma sequência de cadeias de caracteres, uma por linha, e produza uma lista de todas as posições em que cada cadeia possui o mesmo caractere.

Entrada e saída:

A entrada consiste em uma ou mais linhas de caracteres ASCII sem espaço em branco imprimíveis, cada uma seguida por uma nova linha. Você pode supor que todas as linhas de entrada tenham o mesmo comprimento. A nova linha não deve ser considerada parte da entrada (ou seja, você não deve produzi-la como um caractere correspondente).

Exemplo de entrada (roubado descaradamente da pergunta SO):

abcdefg
avcddeg
acbdeeg

Depois de ler a entrada, seu programa deve imprimir as posições de cada coluna correspondente e os caracteres que eles contêm. (Seu programa pode, mas não precisa, parar de ler mais informações se puder determinar antecipadamente que não há colunas correspondentes.) Qualquer formato de saída razoável é permitido; em particular, você pode usar a indexação com base em 0 ou em 1 para as posições.

Exemplo de saída para a entrada acima (usando indexação baseada em 0):

0: a
3: d
6: g

Pontuação:

Isso é código de golfe, então a resposta mais curta vence. Em caso de empate, os caracteres fracionários do desempate podem ser concedidos por recursos adicionais:

  • −½ caracteres para manipular corretamente linhas de entrada de comprimento desigual. (A saída não deve conter posições além do final da linha de entrada mais curta.)
  • - chars para manipular corretamente a entrada que consiste em caracteres Unicode codificados UTF-8 arbitrários.

Para inspiração, você pode encontrar algumas soluções não destruídas na pergunta SO (veja acima).

Esclarecimentos:

  • Simplesmente concatenar as posições e os caracteres, como em 0a3d6g, não conta como "resultado razoável". Você deve fornecer algum tipo de separador (como um espaço) entre cada elemento da saída, para que ele possa ser analisado sem ambiguidade.

  • A entrada será fornecida no fluxo de entrada padrão ( stdin), ou usando qualquer mecanismo de entrada de arquivo de texto mais natural para o seu idioma de escolha. (Se o idioma escolhido não tiver um mecanismo natural para a entrada de arquivos, faça o que parecer mais próximo em espírito.)

  • A entrada termina quando não há mais dados a serem lidos (ou seja, quando ocorre uma condição de fim de arquivo). Se desejar, você pode exigir que a entrada seja finalizada por uma linha em branco (que você então não deve contar como parte da entrada, obviamente). Se você fizer isso, mencione-o na sua resposta para que outras pessoas possam fornecer a entrada correta para o teste.

  • Cada linha de entrada, incluindo a última, termina com um caractere de nova linha. Sua resposta não deve relatar esta nova linha como uma coluna correspondente. (Tudo bem se sua solução também puder manipular entradas onde a última linha não termina em uma nova linha, mas isso não é necessário.)


Então, uma linha em branco encerra a entrada?
Steven Rumbalski

"Você deve fornecer algum tipo de separador entre cada elemento da saída, para que possa ser analisado sem ambiguidade." Um espaço conta como um separador?
Steven Rumbalski

@StevenRumbalski: A entrada termina quando não há mais dados para ler; Acho que posso permitir uma linha em branco à direita se o seu idioma tiver problemas para detectar EOF. E sim, um espaço é um separador perfeitamente bom.
Ilmari Karonen

Podemos ter algum código de amostra arbitrário de caracteres Unicode codificados em UTF-8?
usuário desconhecido

Respostas:


12

APL, 25 caracteres

∩/{0=⍴⍵:⍬⋄(⊂⍵,⍨¨⍳⍴⍵),∇⍞}⍞

Usei o Dyalog APL (versão 13) como intérprete. Ele lida com entradas de comprimento desigual e caracteres Unicode (UTF-8).

Exemplos:

      ∩/{0=⍴⍵:⍬⋄(⊂⍵,⍨¨⍳⍴⍵),∇⍞}⍞
abcdefg
avcddeg
acbdeeg

  1 a  4 d  7 g  

      ∩/{0=⍴⍵:⍬⋄(⊂⍵,⍨¨⍳⍴⍵),∇⍞}⍞
test日本
blat日本国foo

  4 t  5 日  6 本 

Explicação, um pouco da direita para a esquerda:

  • O principal pedaço desta resposta é a função direta (basicamente, função anônima), definida dentro dos chavetas. Seu argumento correto é especificado por .
    • 0=⍴⍵:⍬é a nossa primeira expressão e verifica se obtivemos uma linha vazia (ou seja, terminamos). Ele usa uma proteção (uma construção familiar para muitos programadores funcionais) para executar condicionalmente a expressão à direita dos dois pontos. Nesse caso, se 0 for igual à forma / comprimento ( ) do argumento correto, retornamos o conjunto vazio ( ).
    • separa as duas expressões dentro da função. Se a expressão anterior não foi avaliada (e, portanto, não retornou nada), passamos para a próxima expressão.
    • Chamamos recursivamente a função usando a função de auto-referência ( ). O argumento para a função é uma linha de entrada do usuário não avaliada, fornecida por quote-quad ( ).
    • ⊂⍵,⍨¨⍳⍴⍵ cria pares para cada caractere na sequência, onde o primeiro elemento de cada par é sua posição na sequência e seu segundo elemento é o caractere.
    • ⍳⍴⍵fornece um vetor de 1 a ⍴⍵ou o comprimento da sequência de entrada.
    • ⍵,⍨¨aplica a função de concatenação comutada ( ,⍨) a cada ¨elemento ( ) à sua esquerda ( neste caso, a entrada do usuário) e à direita. A comutação da função de concatenação faz com que seus argumentos esquerdo e direito sejam trocados.
    • Finalmente, incluímos o resultado usando , para que possamos diferenciar as linhas de entrada.
  • Inicialmente, alimentamos nossa função com a entrada do usuário ( ).
  • Finalmente, reduzimos ( /) nosso vetor resultante de vetores de pares usando a função de interseção ( ), produzindo os pares encontrados em todos os subvectores.

Por nenhuma boa razão, tenho uma resposta visceral negativa sempre que vejo a APL que não tenho para J ou GolfScript. Mas +1 de qualquer maneira para uma excelente solução.
Steven Rumbalski

Na verdade, eu estava pensando em mudar para J ... vou abordar isso na minha lista de razões. :)
Dillon Cower

12

Golfscript (28 caracteres)

n/zip:^,,{.^=.&.,1>{;;}*}%n*

Há problemas no conjunto de caracteres ao canalizar Unicode, portanto, nenhum bônus de um quarto de ponto.


1
+1. Isso não deve ter menos votos do que minha resposta.
Steven Rumbalski

9

J, 57 51 44 40 caracteres

,.&.>y;y{{.z[y=.I.*/2=/\]z=.];._2]1!:1]3

Estou chegando devagar, mas com segurança. Ainda está longe de ser o ideal, eu acho.

Eu tinha certeza de que usar um gancho seria a resposta, mas infelizmente não (44 caracteres):

,.&.>((];({{.)~)([:I.[:*/2=/\]))];._2]1!:1]3

Talvez eu precise de um método completamente diferente para diminuir.


1
+1. Mas sim, espero melhor de J.
Steven Rumbalski

Isso não deve ter menos votos do que minha resposta.
Steven Rumbalski

1
@StevenRumbalski Os votos positivos nem sempre refletem os tamanhos relativos do código. Às vezes, se torna um concurso de popularidade de idiomas. Concordo que a resposta golfscript deve estar lá em cima com o APL, infelizmente eu já dei o meu voto positivo e não posso deixar de empurrá-lo ainda mais.
Gareth

8

Haskell, 64 caracteres

main=interact$show.foldl1(filter.flip elem).map(zip[0..]).lines

Lida com linhas de comprimento desigual. O suporte a Unicode depende das configurações atuais de localidade.

Exemplo de saída:

[(0,'a'),(3,'d'),(6,'g')]

+1. Isso não deve ter menos votos do que minha resposta.
Steven Rumbalski

7

Python 2, pontuação 81,5 ( 116 94 86 83 82 bytes menos bônus)

import sys
i=0
for x in zip(*sys.stdin)[:-1]:
 i+=1
 if len(set(x))<2:print i,x[0]

+1 para um bom jogo de golfe em Python, mas você pode perder QUATRO caracteres inteiros: [:-1]não é necessário, a menos que retire uma nova linha estranha no final da entrada (o que nem parece existir na pergunta).
ChristopheD

@ChristopheD: Na verdade, o resultado zip(*sys.stdin)é [('a', 'a', 'a'), ('b', 'v', 'c'), ('c', 'c', 'b'), ('d', 'd', 'd'), ('e', 'd', 'e'), ('f', 'e', 'e'), ('g', 'g', 'g'), ('\n', '\n', '\n')]. Não vejo uma maneira de evitar descartar a última tupla de novas linhas. Por favor, corrija-me se eu entendi errado. Obrigado pelo voto positivo.
Steven Rumbalski

Se você remover a última nova linha do seu arquivo de dados, a tupla dessa linha não estará completa (perde um '\ n', portanto, o zip considera e retorna apenas os dados que estamos procurando, permitindo a remoção de [:-1]. Egzip([1,2,3,4],[1,2,3])=> [(1, 1), (2, 2), (3, 3)]
ChristopheD

@ChristopheD: Por especificação, "a entrada consiste em [...] linhas, [...] cada uma seguida por uma nova linha".
Ilmari Karonen

1
A pessoa que votou negativamente nesta resposta explicaria o porquê?
Steven Rumbalski 18/01/12

5

Script de shell (Bash), 105 caracteres

Se alguém tiver mais alguns truques para isso, preencha gratuitamente para comentar!

for((i=1;i<`tail -1 $1|wc -c`;i++))do
x="cut -c$i $1";y=`$x`;[ `$x|uniq|wc -l` = 1 ]&& echo $i ${y:3};done

Resultado:

1 a
4 d
7 g

Estou tendo problemas para fazer isso funcionar; executar isso na entrada de amostra imprime uma série de erros como /tmp/cols.sh: line 2: [1: command not founde nada mais.
Ilmari Karonen

@Ilmari Karonen: isso foi testado em um Mac (snow leopard, 10.6.2), mas deve funcionar em outro lugar. I'see para tê-lo fixo no Linux amanhã (deve ser uma pequena correção)
ChristopheD

2
ormaaj não tinha o representante, mas queria comentar: ele quebra para Ilmari por causa de um espaço ausente após o [; e o $ {y: 3} fará com que ele funcione apenas com exatamente 3 linhas de entrada. A correção e otimização de rendimentos (100 caracteres) while((++i%`tail -1 $1|wc -c`));do x=`cut -c$i $1`;((`uniq|wc -l`==1))<<<"$x"&&echo $i ${x: -1};donee o uso de valores padrão devem permitir salvar mais um, for((;++i<`tail -1 $1|wc -c`;))domas há um erro não corrigido no bash.
Peter Taylor

4

Perl, 87 caracteres (½ bônus de desempate de char)

Aqui está uma versão em golfe da minha própria solução a partir do thread SO :

chomp($a=$b=<>);$a&=$_,$b|=$_ for<>;@$_=$$_=~/./sgfor a,b;$b[$i++]eq$_&&say"$i:$_"for@a

Ao contrário da versão SO, este usa índices baseados em 1 para a saída. Ele usa o sayrecurso Perl 5.10 , portanto, precisa ser executado com perl -M5.010(ou com perl -E).

Tal como na versão Assim, este código de alças linhas de comprimento variável, e iria manipular a entrada arbitrária Unicode se a entrada e a saída padrão foram em modo UTF-8. Infelizmente, por padrão, não são, a menos que se especifique a opção de linha de comando não livre -CS . Assim, ele ganha o bônus de ½½, mas não o −¼.

Edit: +1 char para corrigir um erro: apenas porque as seqüências de entrada não contêm feeds de linha não significa que elas não podem terminar $a(por exemplo "+" & "J" eq "\n").


1
Você pode salvar 1 caractere usando em chopvez de chomp.
Toto

@ M42: Bom ponto, embora eu goste da robustez da versão atual. Acho que vou manter o mpor enquanto, não é que isso faça alguma diferença no ranking no momento. :)
Ilmari Karonen

3

T-SQL

SELECT N.number, letter = MIN(SUBSTRING(L.line, N.number, 1))
FROM Lines AS L
INNER JOIN master.dbo.spt_values AS N ON N.type = 'P'
WHERE N.number BETWEEN 1 AND (SELECT MAX(LEN(L2.line)) FROM Lines AS L2)
GROUP BY N.number
HAVING COUNT(DISTINCT SUBSTRING(L.line, N.number, 1)) = 1
ORDER BY N.number

2

Scala 115 107: (−¼ para manipulação de UTF-8)

io.Source.stdin.getLines.map(_.zipWithIndex).toList.flatten.groupBy(_._2).map(_._2.toSet).filter(_.size==1)

ungolfed, e em Source.fromFile ("f")vez de stdinpara melhor testabilidade:

io.Source.fromFile ("f").
  getLines.map (_.zipWithIndex).
    toList.flatten.groupBy (_._2). 
      map (_._2.toSet).
        filter (_.size==1)

Resultado:

List(Set((a,0)), Set((g,6)), Set((d,3)))

Agradecimentos a Gareth pela redução do tamanho 8 pelo uso stdin.


Você não pode usar em stdinvez de fromFile("f")salvar 8 caracteres?
Gareth

2

VBA ( 307,25 284 - bônus de 0,75 = 283,25)

Eu sei que isso já foi ganho, mas aqui está a minha chance (não ler um arquivo, apenas uma string - precisa ter o io adicionado). Eu gosto que tenho que usar l()recursivamente. Normalmente, não preciso recursar minha programação da vida real. Eu fiz apenas tantos testes, mas acredito que isso cobre a estipulação de pontos de bônus unicode. Também assumevbCr é o terminador de linha. Isso pode não ser traduzido para todos os sistemas por causa disso.

Código:

Function a(i)
b=Split(Left(i,Len(i)-1),vbCr):c=UBound(b):For q=1 To Len(b(c)):d=Mid(b(c),q,1):If l(b,c,q,d) Then a=a & q & ": " & d & vbCr:Next
End Function
Function l(m, n, o, p)
If n+1 Then l=IIf(o<=Len(m(n)),Mid(m(n),o,1)=p,0) And l(m,n-1,o,p) Else l=Mid(m(n+1),o,1)=p
End Function

Exemplo de entrada / saída:

Debug.Print a("abcdefghijklmnop" & vbCr & "abcdefg" & vbCr & "abcabcghijkl" & vbCr)

1: a
2: b
3: c
7: g

2

Q, 32

{a!((*:)x)a:(&)1=(#:')(?:')(+)x}

uso

q){a!((*:)x)a:(&)1=(#:')(?:')(+)x}[("abcdefg";"avcddeg";"acbdeeg")]
0| a
3| d
6| g

K, 22

A solução acima pode ser reduzida para 22 escrevendo-a completamente em K em vez de passar as funções K para um intérprete Q, reduzindo o número de parênteses necessários.

{a!@[*x]a:&1=#:'?:'+x}

1

PHP, 123 127 :(

Não estou feliz com isso (certamente haverá melhorias a serem feitas), mas aqui vai:

<?$a=$b=trim(fgets(STDIN));while($l=fgets(STDIN)){$a&=$l;$b|=$l;}$n=-1;while(@$a[++$n]){echo$a[$n]!=$b[$n]?'':"$n:{$a[$n]}\n";}

Prova de que funciona.

Se alguém puder pensar em uma maneira mais inteligente de inicializar $ a e $ b, informe-me. Originalmente, eu tinha $a=$b=$n=''e $ b finalmente estava correto, mas [empty] & [anything] == [empty], portanto, $ a nunca teve conteúdo.


Editar: teve que corrigir o tratamento de nova linha (+6), mas deixou cair a marca de fechamento (-2).


Estou curioso, por que você cria a maior parte das respostas na wiki da comunidade?
Gareth

Eu não pretendia fazer isso. Há muito tempo, quando entrei no CodeGolf, alguém me disse que era padrão. Tem que quebrar o hábito. Pode un-wiki agora embora. codegolf.stackexchange.com/a/2249/1419 (ver comentários)
Sr. Llama

Eu acho que era a maneira padrão de fazer as coisas no SO para questões de código de golfe, mas não aqui, caso contrário, ninguém teria nenhuma reputação. :-)
Gareth

Provavelmente, você pode sinalizá-los e pedir a um moderador para desconectá-los . Apenas explique que foi um erro.
Ilmari Karonen

Você pode salvar dois caracteres deixando de fora o ?>. No entanto, observei que seu código possui um erro: ele imprime uma correspondência extra se todas as linhas contiverem uma nova linha à direita, conforme especificado.
Ilmari Karonen

1

JavaScript (125 134 140 )

for(i=s=[];I=s[++i]=prompt(o='');S=I);for(c=-1;w=r=++c<S.length;o+=r?c+':'+C+'\n':'')for(C=S[c];w<i;)r&=s[w++][c]==C;alert(o)

Demonstração: http://jsfiddle.net/Fv7kY/4/

Edição 1 : Reorganize os loops para evitar chaves. Inicialize i com []para combinar com s. Mova o wincremento para a expressão.

Editar 2 : defina S=Ipara capturar a última palavra digitada e salvar usando s[1]. Combine r=1e ++c<S.length. Defina C=s[c]no loop interno e compare com Cas palavras anteriores e próximas, para reduzir a expressão s[w][c]==s[w++][c]para apenas s[w++][c]==C. Guardou um total de 9 caracteres. Também definido w=r=...porque quando isso é verdade, w=1é com isso que precisamos inicializar w.


1

Rubi (71)

a,*s=*$<.lines
(a.size-1).times{|i|s.all?{|t|t[i]==a[i]}&&p([i,a[i]])}

resultado:

[0, "a"]
[3, "d"]
[6, "g"]

Nota: parece requerer o Ruby 1.9; para compatibilidade t[i]com Ruby 1.8, substitua por t[i,1].
Ilmari Karonen

1

Lisp comum, 183 165 caracteres

(let((l(loop for m =(read-line)until(equal m "")collect m)))(loop for c across(car l)for i from 0 if(null(remove c(mapcar(lambda(y)(char y i))l)))collect(list i c)))

Formato legível:

(let ((l (loop for m = (read-line) until (equal m "") collect m)))
  (loop for c across (car l)
        for i from 0 
        if (null (remove c 
                         (mapcar (lambda(y) (char y i))l)))
        collect(list i c)))

Digite isso diretamente no REPL e insira linhas, terminando com uma linha vazia.


1

C, 126 caracteres

char a[999],b[999];main(i){for(gets(a);gets(b);)for(i=0;b[i];++i)a[i]^b[i]?a[i]=0:0;
while(i--)a[i]&&printf("%d:%c\n",i,a[i]);}

Eu estive encarando isso, mas simplesmente não posso torná-lo menor. Uma nova abordagem pode ser necessária.

(Sem pontos de bônus; ele só lida com linhas de tamanhos diferentes se a primeira linha for a mais curta.)


0

C # com .NET 4 (280)

using c=System.Console;class P{static void Main(){char[]a=c.ReadLine().ToCharArray();int r,i,l=a.Length;m:i=0;n:r=c.Read();if(r>0&&r!=10&&r!=13){if((int)a[i]!=r)a[i]='\0';i++;goto n;}for(;i>0&&i<l;)a[i++]='\0';if(r>0)goto m;for(i=0;i<l;i++)if(a[i]!='\0')c.WriteLine(i+":"+a[i]);}}
  • 1 linha, 280 caracteres
  • Inclui todas as instruções de uso necessárias e o método Principal.
  • O programa não requer uma linha vazia no final, mas a aceitará
  • Linhas vazias são ignoradas
  • Manipula cadeias de entrada de qualquer comprimento.
  • Reserva a saída até o final (enquanto a resposta original forneceu saída incremental)

Versão legível

    char[]a=c.ReadLine().ToCharArray();
    int r,i,l=a.Length;
    m:
    i=0;
    n:
    r=c.Read();
    if(r>0&&r!=10&&r!=13){
        if((int)a[i]!=r)
            a[i]='\0';
        i++;
        goto n;
    }
    for(;i>0&&i<l;)
        a[i++]='\0';
    if(r>0)
        goto m;
    for(i=0;i<l;i++)
        if(a[i]!='\0')
            c.WriteLine(i+":"+a[i]);

Resposta original

usando c = System.Console; classe P {static void Main () {char [] a; var b = c.ReadLine (); a = b.ToCharArray (); while (b! = "") {for (int i = 0; i

  • 1 linha
  • 207 caracteres
  • Inclui todas as instruções de uso necessárias e o método Principal.
  • O programa termina quando uma linha vazia é inserida.
  • Não manipula seqüências de entrada mais curtas que a primeira.


Versão legível:

    static void Readable()
    {
        char[]a;
        string b=System.Console.ReadLine();
        a=b.ToCharArray();
        while(b.Length>0)
        {
            for (int i = 0; i < a.Length; i++)
            {
                if (a[i] != b[i])
                {
                    a[i] = '\0';
                }
                else
                {
                    System.Console.WriteLine(i+": "+a[i]);
                }
            }
            b=System.Console.ReadLine();
        }
    }


Quando executo isso na entrada de teste do desafio, recebo 0: a 1: b 2: c 3: d 4: e 5: f 6: g 0: a 2: c 3: d 6: g 0: a 3: d 6: g. O resultado esperado seria 0: a 3: d 6: g.
Ilmari Karonen

@ Ilmari Ok, mas ele gera as colunas / caracteres que são os mesmos após cada linha de entrada. Se você estiver alimentando um arquivo como entrada padrão, a saída poderá parecer estranha, mas se você inserir manualmente, acho que faz sentido. Vou considerar como refatorar, no entanto.
Aprendiz do Dr. Wily

Sua solução falha se alguma linha for maior que a primeira.
Timwi

@ Timwi Ah ... obrigado por apontar isso!
Aprendiz do Dr. Wily

0

python 122 caracteres :

print("\n".join([str(i)+':'+str(x[0]) for i,x in enumerate(zip(*[tuple(x) for x in input().split()])) if len(set(x))<2]))

você não precisa de um espaço entre )e for. Então, em vez de …str(x[0]) for i,x…, você pode fazer …str(x[0])for i,x…. Ele também aparece em tuple(x) fore.split()])) if
Cyoce 04/04

-1

Rubi (242)

s = %w{ abcdefg avcddeg acbdeeg aejdjeggd }
cols = []
s.sort{ |a, b| b.size <=> a.size }[0].size.times do |i|
  uneq=true
  l = s[0][i]
  s.each { |w| uneq = false if l != w[i] }
  cols << [l, i] if uneq
end
cols.each { |c| puts c.join('|') }

A intenção do desafio era ler as linhas da entrada padrão. Estou disposto a reduzir algumas folgas para idiomas (como JavaScript no navegador) onde esse conceito realmente não existe, mas Ruby tem STDIN( ARGFou simplesmente gets).
Ilmari Karonen

Ah ok. Mas considerando que o STDIN aceita uma linha, deve-se assumir algo como: "Digite outra linha, ou 'n' para parar"? Portanto, crie um loop para criar uma matriz.
precisa saber é

Eu adicionei alguns esclarecimentos à pergunta. Basicamente, você deve continuar lendo as linhas de entrada até chegar ao final do arquivo.
Ilmari Karonen 19/01/12

você tem uma tonelada de espaço em branco desnecessário.
Cyoce 04/04

-1

C #

List<string> strings = new List<string> { "abcdefg", "avcddeg", "acbdeeg", "aejdjeggd" };
var transposes = from index in Enumerable.Range(0, strings.First().Length)
                 select new string((from s in strings select s[index]).ToArray());
int i = 0;
foreach(string transpose in transposes)
{
   if (transpose.Distinct().Count() == 1)
     Console.WriteLine("{0}: {1}", i , transpose[0]);
   i++;
}

1
Oi, Arjang, e bem-vindo ao codegolf.SE! Alguns comentários sobre sua resposta: Primeiro, como esse é um desafio do código-golfe , espera-se que você tente tornar sua solução o mais curta possível; só para começar, você tem alguns nomes longos de variáveis ​​que podem ser facilmente reduzidos para caracteres únicos e algum espaço em branco em excesso que você pode remover. (Não há problema em publicar uma versão legível do seu código junto com a versão "golfed", mas você também deve postar uma solução golfed.) Segundo, se você ler a pergunta com atenção, especifiquei que você deveria ler as strings da entrada padrão , não codifique-os.
Ilmari Karonen
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.