Vérifiez s'il existe une sous-chaîne isomorphe

12

Cette question est une extension de Vérifier si les mots sont des isomorphes et en copie la première partie pour donner la définition d'un isomorphe.

Deux mots sont isomorphes s'ils ont le même schéma de répétitions de lettres. Par exemple, les deux ESTATEet DUELEDont un motifabcdca

ESTATE
DUELED

abcdca

parce que les lettres 1 et 6 sont les mêmes, les lettres 3 et 5 sont les mêmes, et rien de plus. Cela signifie également que les mots sont liés par un chiffre de substitution, ici avec la correspondance E <-> D, S <-> U, T <-> E, A <-> L.

Étant donné deux chaînes X et Y, avec X ne dépassant pas Y, la tâche consiste à déterminer s'il existe une sous-chaîne de Y qui est un isomorphe avec X.

Entrée: Deux chaînes de lettres non vides de a..zA..Z dont une chaîne par ligne. Cela proviendra de l'entrée standard.

Sortie Une sous-chaîne de la deuxième chaîne qui est un isomorphe avec la première chaîne, si une telle chose existe. Sinon, affichez "Non!".

Règles Votre code doit s'exécuter en temps linéaire sur la longueur totale de l'entrée.

Score Votre score est le nombre d'octets dans votre code. Le moins d'octets gagne.


Exemple d'entrée et de sortie

adca
ddaddabdaabbcc

dabd

Pointe

Il existe au moins une solution temporelle pas si compliquée, pratiquement rapide et linéaire à ce problème.


la source
@AlexA. Je pense que quelqu'un m'a dit que si vous limitez le temps d'exécution / la complexité, cela devrait être un défi de code. Je suis heureux de le changer si c'est faux, bien sûr.
7
Si le temps d'exécution est limité par une règle et n'influence pas le score, le code-golf est mieux adapté que le code-challenge.
Dennis
le temps linéaire signifie qu'il doit être O (m + n) et non O (mxn) ni O (mx (nm)) où m, n sont la longueur de la première et de la deuxième chaîne?
un utilisateur
@someuser Oui, cela signifie O (m + n).
1
@BetaDecay See "Étant donné deux chaînes X et Y, avec X ne dépassant pas Y, la tâche consiste à déterminer s'il existe une sous-chaîne de Y qui est un isomorphe avec X."

Réponses:

8

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 avec 0 statut de sortie)

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

L'algorithme est une variante de KMP, utilisant un test basé sur un index pour la correspondance des caractères. L'idée de base est que si nous obtenons un décalage à la position, X[i]nous pouvons retomber à l'endroit suivant possible pour une correspondance selon le suffixe le plus long X[:i]qui est isomorphe à un préfixe de X.

De gauche à droite, nous attribuons à chaque caractère un index égal à la distance à l'occurrence précédente la plus récente de ce caractère, ou s'il n'y a pas d'occurrence précédente, nous prenons la longueur du préfixe de chaîne actuel. Par exemple:

MISSISSIPPI
12313213913

Pour tester si deux caractères correspondent, nous comparons les indices en ajustant de manière appropriée les indices supérieurs à la longueur de la (sous-) chaîne actuelle.

L'algorithme KMP devient un peu simplifié car nous ne pouvons pas obtenir une différence sur le premier caractère.

Ce programme génère la première correspondance s'il en existe une. J'utilise une erreur d'exécution pour quitter en cas de correspondance, mais le code peut être facilement modifié pour quitter proprement au prix de quelques octets.

Remarque: Pour le calcul des indices, nous pouvons utiliser str.rfind(contrairement à mon approche précédente en utilisant un dictionnaire) et avoir toujours une complexité linéaire, en supposant que str.rfindcommence la recherche depuis la fin (ce qui semble être le seul choix d'implémentation sain) - pour chaque caractère de l'alphabet , nous n'avons jamais à parcourir deux fois la même partie de la chaîne, il y a donc une limite supérieure de comparaison (taille de l'alphabet) * (taille de la chaîne).

Étant donné que le code est devenu assez obscur au cours du golf, voici une solution antérieure (293 octets) qui est un peu plus facile à lire:

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

La efonction teste l'équivalence des caractères. L' execinstruction assigne des indices et effectue quelques initialisations variables. La première boucle traite Xles valeurs de repli et la seconde boucle effectue la recherche de chaîne.

Mise à jour: voici une version qui se termine proprement, au prix d'un octet:

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
Mitch Schwartz
la source
Je pense que vous pouvez enregistrer un octet en faisant python3. r=raw_inputvs r = inputenregistre 4 octets et les parens sur l'impression ne coûtent que 3.
Maltysen
@Maltysen merci pour le commentaire. Je n'ai pas pris en compte Python 3 lors de l'écriture, mais en ce qui concerne les coûts et les économies, il y a un coût supplémentaire de 2 octets car vous ne pouvez pas mélanger les espaces et les tabulations pour l'indentation dans Python 3.
Mitch Schwartz
@Maltysen juste pour faire un suivi, le problème n'est plus des onglets mais des crochets pour exec.
Mitch Schwartz
C'est une très bonne réponse! J'ai hâte de le voir en cjam maintenant :)
6

Python 3, 401 octets

import string,itertools
X=input()
Y=input()
x=len(X)
t=[-1]+[0]*~-x
j=2
c=0
while j<x:
 if X[j-1]==X[c]:c+=1;t[j]=c;j+=1
 elif c>0:c=t[c]
 else:t[j]=0;j+=1
s=string.ascii_letters
*b,=map(s.find,X)
for p in itertools.permutations(s):
 m=i=0
 while m+i<len(Y):
  if p[b[i]]==Y[m+i]:
   if~-x==i:print(Y[m:m+x]);exit()
   else:i+=1
  else:
   if-1<t[i]:m+=i-t[i];i=t[i]
   else:i=0;m+=1
else:print("No!")

C'est encore en grande partie non golfé, mais je pense que cela devrait fonctionner. L'algorithme de base est KMP , plus un facteur supplémentaire qui est factoriel dans la taille de l'alphabet (ce qui est bien, car l'alphabet est constant). En d'autres termes, il s'agit / devrait être un algorithme linéaire complètement impraticable.

Voici quelques annotations pour aider à l'analyse:

# KMP failure table for the substring, O(n)
t=[-1]+[0]*~-x
j=2
c=0
while j<x:
 if X[j-1]==X[c]:c+=1;t[j]=c;j+=1
 elif c>0:c=t[c]
 else:t[j]=0;j+=1

# Convert each char to its index in a-zA-Z, O(alphabet * n)
s=string.ascii_letters
*b,=map(s.find,X)

# For every permutation of letters..., O(alphabet!)
for p in itertools.permutations(s):
 # Run KMP, O(n)
 m=i=0
 while m+i<len(Y):
  if p[b[i]]==Y[m+i]:
   if~-x==i:print(Y[m:m+x]);exit()
   else:i+=1
  else:
   if-1<t[i]:m+=i-t[i];i=t[i]
   else:i=0;m+=1
else:print("No!")

Pour les tests, vous pouvez remplacer spar un alphabet plus petit que string.ascii_letters.

Sp3000
la source
2

APL (Dyalog) , 32 octets

Il s'agit d'une fonction d'infixe, prenant X comme argument de gauche et Y comme argument de droite.

{(s,⊂'No!')⊃⍨(⍳⍨¨s←⍵,/⍨≢⍺)⍳⊂⍳⍨⍺}

Essayez-le en ligne!

{} Lambda anonyme où et représentent les arguments (X et Y)

⍳⍨⍺ɩ selfie ndex de X ( ɩ ndices de la première occurrence des éléments de X dans X)

 joindre afin que nous puissions rechercher ce motif entier

(... )⍳ɩ ndex de la première occurrence de ce dans ...

  ≢⍺ décompte (longueur) de X

  ⍵,/⍨ toutes les sous-chaînes de cette taille de Y (lit. réduction de concaténation de celles-ci, mais c'est un no-op)

  s← stocker dans s(pour s ubstrings)

  ⍳⍨¨ɩ selfie ndex de chacun de ceux

 nous avons maintenant l'index du premier motif, ou 1 + le nombre de motifs si aucune correspondance n'a été trouvée

()⊃⍨ Utilisez cet indice pour choisir parmi…

  ⊂'No!' la chaîne incluse (pour qu'elle fonctionne comme un seul élément)

  s, ajouté avec s

Adam
la source