elenco di parole più lungo con lettere iniziali e finali corrispondenti


11

Il mio amico mi ha dato un problema che dice sia facile, ma non riesco a capire un buon algoritmo da usare per farlo.

Ti viene dato un input di 100 parole inglesi casuali. Devi trovare la stringa di parole più lunga in cui l'ultima lettera in una parola corrisponde alla prima lettera nella parola successiva. Puoi usare ogni parola una sola volta.

Ad esempio, se ti venissero date le parole "gatto", "cane", "quello", la stringa più lunga che potresti creare sarebbe "gatto -> quello". Se ti venissero date le parole "mouse", "alce", "unicorno", la stringa più lunga che potresti creare sarebbe solo una parola (dal momento che nessuna di queste parole collega). Se ti venissero date le parole "uccello", "parabola", "arbusto", la stringa più lunga che potresti creare sarebbe "arbusto -> uccello -> piatto" (o "piatto -> arbusto -> uccello" o "uccello - > dish -> harb ").

Mi è venuta l'idea di modellarlo come un grafico ciclico diretto. Ogni nodo sarebbe solo una parola, con i vertici che vanno a ciascuna parola / nodo che inizia con la lettera con cui termina questa parola.

+-------+         \ +------+
|  cat  |-----------| that |
+-------+         / +------+
    |                  |
   \|/                 |
+-------+ /            |
|  the  |--------------+
+-------+ \

Questo problema sembra essere una ricerca del percorso più lungo , che è NP-Hard.

C'è un modo migliore per farlo? O anche una sorta di algoritmo di approssimazione che potrebbe essere usato? O un modo per sfruttare le qualità dell'inglese per ridurre lo spazio di ricerca?


4
Con 100 parole, ottieni (almeno) 100! = 9.332622e + 157 combinazioni. Buona fortuna, penso che il tuo amico ti stia prendendo in giro dicendo che è facile.
Martin Wickman,

1
Ma il numero di possibili combinazioni è molto inferiore a quello, perché in media una singola parola è collegata solo a circa 6 o 7 altre parole.
Abe Tool

2
Hai ragione sul fatto che questa è esattamente una ricerca del percorso più lungo. Penso che il tuo amico abbia torto. Tuttavia, una ricerca esaustiva non è difficile da codificare e potrebbe non durare così a lungo.
Kevin Cline,

4
Solo per divertimento, ho scritto una ricerca esaustiva sulla forza bruta (come ha sottolineato @kevincline) in Ruby ( gist.github.com/anonymous/6225361 ). Con 100 parole, sono bastati ~ 96 secondi ( gist.github.com/anonymous/6225364 ). E questo era uno script altamente inefficiente, non ottimizzato, in linguaggio interpretato, veloce e sporco. Quindi, con solo 100 parole, anche una versione lenta della forza bruta viene eseguita in un tempo ragionevole. Il mio codice in realtà non crea un grafico aciclico e quindi cerca attraverso di esso, costruisce in modo ricorsivo ogni possibile percorso a partire da ogni parola e tiene traccia di quelli più lunghi.
Ben Lee,

3
Il problema afferma che ci sono 100 parole. Penso che questo significhi che puoi applicare una soluzione di programmazione dinamica, che è menzionata nell'articolo a cui ti riferisci.
Julien Guertault,

Risposte:


5

Penso che questo sia legato al problema del percorso più lungo (LP) che hai menzionato, ma è un po 'diverso. La differenza principale è che il problema LP ha un grado di connettività più elevato rispetto a quello del problema suggerito. Limitando le connessioni all'ultima e alla prima lettera, si rimuove un gran numero di potenziali combinazioni.

Ecco come consiglierei di affrontare questo:

  1. Per ogni parola nell'elenco, conta le possibili connessioni in entrata e in uscita.
  2. Elimina tutte le parole che hanno 0 in e 0 out.
  3. Identificare un insieme iniziale di "parole di partenza" con il numero più basso di entrate e uscite e le uscite devono essere maggiori di 0.
  4. Ogni parola di partenza riceve la propria copia di lavoro del conteggio delle connessioni in / out. Questo costituisce il capo della catena.
  5. Per ogni catena, identifica un elenco di "parole successive" basato su:
    • ultima lettera di avviamento o parola precedente
    • minor numero di connessioni interne ed esterne (di nuovo, le uscite devono essere maggiori di 0)
  6. Per ciascuno next word, ripetere il passaggio 5 fino al termine della catena.

Tieni presente che:

  • Dovrai tenere traccia della lunghezza delle catene e disporre di un meccanismo globale per identificare la catena più lunga.

  • Sarà inoltre necessario rimuovere ogni parola dalla copia di lavoro dei conteggi delle connessioni per evitare un ciclo ricorsivo.

  • A un certo punto, la catena terminerà e dovrai selezionare una parola con un conteggio di 0 connessioni.

  • Potrebbe essere necessario ricalcolare i dettagli in quanto le parole vengono rimosse dagli elenchi di lavoro. A prima vista, non penso che questo sarà necessario in quanto i set complessivi saranno relativamente piccoli. Se hai ridimensionato a 1000 parole, avere conteggi statici potrebbe rallentare la convergenza dell'algoritmo.

Ho visto questo come un problema di imballaggio. Per me, le connessioni in entrata e in uscita identificano la forma da imballare. Più basse sono le connessioni, più strana è la forma. Quanto più strana è la forma, tanto prima voglio confezionarla poiché ho percepito una probabilità in diminuzione di riuscire a impacchettare una forma strana più tardi sono entrato nella catena.

Come esempio:

{dog, gopher, alpha, cube, elegant, this, that, bart}

dog     0, 1
gopher  1, 0
alpha   0, 0
cube    0, 1
elegant 1, 2
this    3, 0
that    2, 1
bart    0, 2

//alpha is dropped with 0 in and 0 out.
//two candidates found: dog, cube

//chain 1
dog => gopher
//chain 2
cube => elegant => that => this

//Note 1: the following chain won't occur due to selection rules
//that takes priority over this because of output count
cube => elegant => this

//Note 2: this chain won't occur either due to selection rules
bart => that => this

2
C'è qualche garanzia che questo algoritmo troverà sempre il percorso più lungo? Al di fuori della mia testa, non riesco a pensare a un contro-esempio, ma sembra che potrebbe cadere per una soluzione di tipo "massimo locale".
Ben Lee,

@BenLee - Sono un ingegnere del software; Non garantisco mai il mio codice. :-) Scherzi a parte, non conosco la risposta alla tua domanda. La mia teoria degli insiemi e le capacità di prova matematica sono deboli, per dirla in parole povere, quindi non ho altro modo che la valutazione empirica per convalidare il mio algoritmo. Non sono sicuro che questo problema sia davvero NP-difficile, ma non posso nemmeno confermare tale affermazione. Se non è NP-difficile, dovrebbe esserci un mezzo per convalidare l'algoritmo.

2
Che dire di un elenco di parole come questo: "cane, gopher, crocchia, suora, mezzogiorno, nub". L'algoritmo sceglierebbe erroneamente l'elenco più lungo come "cane -> gopher", quando in realtà è una combinazione di "bun, nun, mezzogiorno, nub".
Abe Tool,

1
@AbeTool - buon esempio lì. Vorrei aggiungere un'altra iterazione (o due) per consentire le combinazioni "input più basso> = 1" e "output più basso> = 1".

2
Non credo che risolverà il problema in tutti i casi. Penso che questo rientri in una soluzione di tipo "massimo locale".
Abe Tool

3

Se si crea una matrice 26X26 per rappresentare il grafico diretto del vertice come ogni alfabeto e le parole come bordo. Ad esempio parola - APPLE collega i vertici A ed E con il bordo diretto da A a E. Ora il problema si riduce a trovare la traccia Euleriana più grande (percorso che include il numero massimo di bordi, visitando ogni bordo una volta con possibile ripetizione di vertici) nel grafico. Uno degli algoritmi O (E) sarebbe quello di iniziare in modo casuale da una coppia di vertici. Trova un percorso tra di loro. Di continuare a rilassare il percorso fino a quando è possibile.

update @ GlenH7 Ho risolto una domanda simile su www.hackerearth / jda di recente, c'erano punti relativi rispetto alla migliore soluzione e ho ottenuto il punteggio più alto con il seguente approccio-

Elenco di parole dato. Trova la catena più lunga che può essere formata da loro. Una catena è valida se ogni parola inizia con una lettera * che termina alla fine dell'ultima parola.

Approccio =

1) crea il grafico degli alfabeti come vertici e le parole come spigoli. Al posto dell'uso di più spigoli utilizzare uno con peso uguale al numero di spigoli.

2) trova il componente fortemente connesso del grafico con i bordi massimi. Scarta temporaneamente gli altri bordi.

3) Per ogni vertice rendere la sua indegree uguale al suo outdegree.

4) Ora esiste il loro circuito euleriano nel grafico. Trovalo.

5) Ora nel grafico rimanente (grafico originale rosso trova la scia più lunga con il primo vertice nel componente fortemente connesso scelto. Penso che questo sia NP difficile.

6) Includi la traccia sopra nel circuito eleriano convertendo il circuito euleriano in pista.

Perché - Accetto che questa domanda sia molto probabilmente NP difficile (suppongo, non matematicamente parlando). Ma l'approccio sopra funziona meglio quando c'è una lunga lista (1000+) di parole distribuite uniformemente (cioè non inteso come wc per l'approccio sopra). Supponiamo che dopo aver convertito un determinato elenco in un grafico sopra menzionato, per fortuna si rivela un grafico euleriano (vedi http://en.wikipedia.org/wiki/Eulerian_path per le condizioni), quindi senza dubbio possiamo dire che rispondi alla domanda sopra è P ed è in realtà il percorso euleriano nel grafico (vedi http://www.graph-magics.com/articles/euler.php per un approccio molto semplice per farlo e vedere questo per verificare che il tuo grafico abbia singolo http://www.geeksforgeeks.org/strongly-connected-components/e se non pulire temporaneamente altri piccoli scc perché esiste un percorso euleriano per singolo scc). Quindi per i casi non fortunati (che sono quasi tutti i casi) provo a convertirli in casi fortunati (cioè le condizioni del sentiero euleriano sono soddisfatte). Come fare questo? Ho provato a fare una ricerca di profondità crescente per i bordi irrilevanti (l'insieme dei bordi in un percorso che fissa dal vertice con livello superiore maggiore di livello inferiore e termina al vertice con livello superiore maggiore di livello inferiore). L'aumento della ricerca della profondità significa che per prima cosa ho cercato tutte queste serie di un bordo nel percorso rispetto a due bordi nel percorso e così via. A prima vista potrebbe sembrare che la sua ricerca approfondita richiederebbe O (nodi ^ i), quindi la complessità temporale totale di O (nodi + nodi ^ 2 + nodi ^ 3 + ....) fino a quando non sarà un caso fortunato. Ma l'analisi ammortizzata rivelerà che è O (bordi). Una volta ridotto il caso fortunato trova il circuito euleriano.

Fino a qui era tutto il tempo polinomiale. Ciò darebbe quasi la soluzione migliore. Ma per aumentare ulteriormente la tua soluzione (la soluzione perfetta è NP difficile) prova un approccio avido nel grafico rimanente per trovare una lunga scia che fissa con uno dei vertici nella scc scelta. Ora aggiungilo alla traccia euleriana sopra trovata per aumentarla ulteriormente.


@ GlenH7 Di recente ho risolto una domanda simile su www.hackerearth / jda, c'erano dei voti relativi rispetto alla migliore soluzione e ho ottenuto il punteggio più alto con il seguente approccio-
vishfrnds

0

Idea:

Innanzitutto, crea due mappe (hash), ad esempio S ed E, dalle lettere dell'alfabeto alle parole; il primo, S, associa le lettere iniziali alle parole, il secondo, E, fa lo stesso con le lettere finali.

Ad esempio, se il dizionario è composto da:

uccello, piatto, cane, arpa

noi abbiamo:

S:

a -> [ ]
b -> [ bird ]
c -> [ ]
d -> [ dish, dog ]
...
h -> [ harb ]
...

e,

E:

a -> [ ]
b -> [ harb ]
c -> [ ]
d -> [ bird ]
...
g -> [ dog ]
h -> [ dish ]
...

Quindi, usando S ed E per le ricerche rapide, crea una foresta (insieme di alberi), delle stesse dimensioni del dizionario, con le radici in ogni parola e non consentendo a una parola di apparire più di una volta in un albero - memorizza nella cache il profondità degli alberi mentre le costruisci:

bird (depth: 2)
   dish
      harb
   dog

dish (depth: 3)
   harb
      bird
         dog

dog (depth: 0)

harb (depth: 2)
   bird
      dish
      dog

Infine, scorrere la foresta e trovare l'albero (i) di maggiore profondità.

Le soluzioni saranno sull'asse discendente di quegli alberi.

Per esempio,

dish / harb / bird / dog

sopra.

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.