Python 2, 338 326 323 321 310 306 297 293 290 289 280 279 266 264 259 237 230 229 226 223 222 220 219 217 ( 260 238 231 228 225 223 221 220 218 com status de saída 0)
exec'''s=raw_input()
S=[M-s.rfind(c,0,M)for M,c in enumerate(s)]
k=0
j=x=%s
while k<=M+x:
if S[k]>j<W[j]or S[k]==W[j]:
k+=1;j+=1;T+=[j]
if j-L>x:print s[k-j:k];z
else:j=T[j]
'''*2%('-1;T=[0];W=S;L=M',0)
print'No!'
O algoritmo é uma variação do KMP, usando um teste baseado em índice para correspondência de caracteres. A idéia básica é que, se tivermos uma incompatibilidade na posição X[i]
, podemos voltar ao próximo local possível para uma partida, de acordo com o sufixo mais longo X[:i]
que é isomórfico para um prefixo de X
.
Trabalhando da esquerda para a direita, atribuímos a cada caractere um índice igual à distância para a ocorrência anterior mais recente desse caractere, ou, se não houver ocorrência anterior, assumimos o comprimento do prefixo da string atual. Por exemplo:
MISSISSIPPI
12313213913
Para testar se dois caracteres correspondem, comparamos os índices, ajustando-os adequadamente para índices maiores que o comprimento da (sub) sequência atual.
O algoritmo KMP se torna um pouco simplificado, pois não podemos obter uma incompatibilidade no primeiro caractere.
Este programa gera a primeira correspondência, se houver. Uso um erro de tempo de execução para sair no caso de uma correspondência, mas o código pode ser facilmente modificado para sair de forma limpa, ao custo de alguns bytes.
Nota: Para índices de computação, podemos usar str.rfind
(em oposição à minha abordagem anterior usando um dicionário) e ainda ter complexidade linear, supondo que str.rfind
comece a pesquisar a partir do final (que parece a única opção de implementação sensata) - para cada caractere no alfabeto , nunca precisamos percorrer a mesma parte da string duas vezes, para que haja um limite superior das comparações (tamanho do alfabeto) * (tamanho da string).
Como o código ficou ofuscado durante o golfe, eis uma solução anterior (293 bytes) que é um pouco mais fácil de ler:
e=lambda a:a>i<W[i]or a==W[i]
exec('s=raw_input();S=[];p={};M=i=0\nfor c in s:S+=[M-p.get(c,-1)];p[c]=M;M+=1\nW=S;L=M;'*2)[:-9]
T=[0]*L
k=1
while~k+L:
if e(W[k]):i+=1;k+=1;T[k]=i
else:i=T[i]
m=i=0
while m+i<M:
if e(S[m+i]):
if~-L==i:print s[m:m+L];z
i+=1
else:m+=i-T[i];i=T[i]
print'No!'
A e
função testa a equivalência de caracteres. A exec
instrução atribui índices e faz algumas inicializações variáveis. O primeiro loop processa os X
valores de retorno e o segundo loop faz a pesquisa de cadeias.
Atualização: Aqui está uma versão que sai corretamente, ao custo de um byte:
r='No!'
exec'''s=raw_input()
S=[M-s.rfind(c,0,M)for M,c in enumerate(s)]
k=0
j=x=%s
while k<=M+x:
if S[k]>j<W[j]or S[k]==W[j]:
k+=1;j+=1;T+=[j]
if j-L>x:r=k=s[k-j:k]
else:j=T[j]
'''*2%('-1;T=[0];W=S;L=M',0)
print r