Trovare serpenti in una matrice


32

Sfida

Data una matrice binaria e una stringa binaria, determinare se tale stringa binaria può essere trovata a partire da qualsiasi punto della matrice e spostandosi in qualsiasi direzione in qualsiasi punto successivo per formare la stringa binaria. Cioè, la stringa può essere trovata piegata ma all'interno della matrice?

La corda può essere piegata solo a 90 gradi o 180 gradi (connessioni ai bordi; Manhattan Distance 1) e non può sovrapporsi in alcun punto.

Esempio

Facciamo il seguente esempio:

Matrix:

010101
111011
011010
011011

Snake: 0111111100101

Questo è un vero caso di prova. Possiamo vedere il serpente piegato nella seguente posizione:

0-1 0 1 0 1
  |
1 1 1-0 1 1
  | | |   |
0 1 1 0-1-0
  | |
0 1-1 0 1 1

Regole

  • Si applicano scappatoie standard
  • Se lo desideri, puoi prendere la lunghezza della stringa e la larghezza e l'altezza della matrice come input
  • È possibile prendere la matrice binaria e la stringa binaria come una stringa multilinea / matrice di stringhe / stringa unita a newline / qualsiasi altra stringa unita e una stringa
  • È possibile prendere le dimensioni come una matrice piatta anziché diversi argomenti
  • Il programma deve terminare per qualsiasi matrice 5 x 5 con una stringa fino a 10 in meno di un minuto

limitazioni

  • La matrice non è necessariamente quadrata
  • La stringa sarà non vuota
  • La stringa può essere lunghezza-1
  • La stringa non conterrà più quadrati di quelli disponibili (ovvero len(string) <= width(matrix) * height(matrix)

Casi test

Truthy

01010
10101
01010
10101
01010

0101010101010101010101010



01110
01100
10010
10110
01101

011111000110100



0

0



10
01

1010



100
010
001

100010001

Falsy

00000
00000
00000
00000
00000

1



10101
01010
10101
01010
10101

11



100
010
001

111



10001
01010
00100
01010
10001

1000100010001000101010100


4
Oppure: Binary Boggle! Inoltre, puoi aggiungere qualche altro test case?
Giona,

1
Che cosa significa piatto, nitido e rotondo in questo contesto? Il quadrato non significa che la larghezza e l'altezza potrebbero non essere uguali o che l'array può essere frastagliato?
Tahg,

quello che diavolo è un array circolare
Conor O'Brien,

Risposte:


13

Python 2 , 275 271 264 249 byte

  • Salvato quattro byte sostituendo -1 con He rimuovendo un'operazione di slicing ( [:]).
  • Salvataggio di sette byte grazie a Halvard Hummel ; rimuovendo ancora un'altra operazione di slicing ( [:]), usando l'assegnazione multi-target per dare un valore a una voce visitata v not in "01"( S=S[1:];M[y][x]=H;->S=M[y][x]=S[1:]; ) e passando da un se ternario se / else a un semplice logico o ( any(...)if S else 1-> not S or any(...)).
  • Se estendi in qualche modo la tua definizione di verità e falsità , potresti consentire questa soluzione lunga 257 byte . Solleva un'eccezione ( ZeroDivisionError) quando viene trovato il serpente e restituisce un elenco vuoto ( []) quando non è stato trovato alcun serpente, che sono due comportamenti distinti.
  • Risparmiato quattordici byte grazie a user202729 ; golf due copie profonde di matrice
  • Salvato un byte; giocato not S ora S<[1]or~ S==[]or.
lambda M,S,w,h:any(H(eval(`M`),S,w,h,x,y)for y in range(h)for x in range(w)if S[0]==M[y][x])
def H(M,S,w,h,x,y):S=M[y][x]=S[1:];return S<[1]or any(H(eval(`M`),S,w,h,x+X,y+Y)for X,Y in[(~0,0),(1,0),(0,~0),(0,1)]if~0<x+X<w>0<=y+Y<h!=S[0]==M[y+Y][x+X])

Provalo online!

Spiegazione

Funzione lambda che accetta la matrice come un elenco bidimensionale di stringhe (o "0"o "1"), il serpente come un elenco unidimensionale e le dimensioni della matrice come due numeri interi.
La funzione lambda cerca nella matrice le voci che corrispondono al primo elemento del serpente. Per ogni partita trovata, chiama Hcon una copia profonda della matrice, nessuna copia del serpente, le dimensioni della matrice e la posizione della partita.

Quando Hviene chiamato, rimuove Sla prima voce e imposta la voce matrice della posizione data su qualcosa di diverso da "0", "1". Se S'lunghezza è zero, ritorna True; come si chiama ricorsivamente, il serpente è stato trovato da qualche parte nella matrice.
Se S'lunghezza è diversa da zero, scorre attraverso le quattro direzioni cardinali, verifica se quella posizione si trova nella matrice, confronta l'elemento matrice in quella posizione con il primo elemento di Se - se corrisponde - si chiama ricorsivamente.
HI valori di ritorno sono incanalati nei frame dello stack, controllando sempre se almeno una funzione ha trovato un possibile serpente.

Uscita formattata

Ho aumentato il mio programma per produrre anche il percorso che il serpente striscia (se ce n'è uno). Utilizza lo stesso design di output ASCII della domanda. Collegamento TIO .



1
@HalvardHummel Grazie; soprattutto per individuare l'operazione di taglio superflua.
Jonathan Frech,

@ user202729 Pensi m[:]for~> m*1for? Potrebbe funzionare.
Jonathan Frech,

@ user202729 Grazie, la punta collegata ha funzionato perché penso che questo abbia bisogno di una copia profonda.
Jonathan Frech,

9

JavaScript (ES6), 138 134

Non così diverso da @ Neil's, ma cos'altro potrebbe essere?

Input: matrice come stringa a più righe, stringa binaria, larghezza (senza contare la riga nuova)

NB: la logica nella funzione ricorsiva rè in qualche modo invertita per salvare un paio di byte

(m,s,w)=>[...m].some((c,p,m,r=(p,o)=>s[m[p]=r,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))

Meno golf

(m,s,w)=>(
  m=[...m],
  r= (p, o) => 
    (m[p] = -w, s[o])
    && (
         [~w, -~w, 1, -1].every( d =>
            m[d+=p] != s[o] || r(d, o+1)
         )
         && (m[p]=s[o-1])
    ),
  m.some((c,p) =>c == s[0] && !r(p,1))
)

Test

var F=
(m,s,w)=>[...m].some((c,p,m,r=(p,o)=>s[m[p]=r,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))

// this slightly modified version tracks the path
var Mark=
(m,s,w)=>(m=[...m]).some((c,p,m,r=(p,o)=>s[m[p]=-o,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))
?m.map((c,p)=>c<-1?'.───│┘└.│┐┌.│'[((m[p-1]-c)**2<2)+((m[p+1]-c)**2<2)*2+((m[p+~w]-c)**2<2)*4+((m[p-~w]-c)**2<2)*8]:c<0?'*':c).join``:''

function go()
{
  O.textContent =F(M.value, S.value, M.value.search('\n'))+'\n\n'
  +Mark(M.value, S.value, M.value.search('\n'))
}

go()
#M {width:100px; height:100px }
<textarea id=M>010101
111011
011010
011011</textarea><br>
<input id=S value='0111111100101' oninput='go()'>
<button onclick='go()'>go</button>
<pre id=O></pre>


6

JavaScript (ES6), 149 byte

(m,s,w)=>[...m].some((c,i)=>c==s[0]&&g(m,s,i),g=(m,s,i)=>!(s=s.slice(1))||[~w,-1,1,-~w].some(o=>m[o+=i]==s[0]&&g(m.slice(0,i)+' '+m.slice(i+1),s,o)))

Prende la matrice come stringa delimitata da nuova riga, il serpente come stringa e la larghezza (come numero intero). Liberamente basato sulla risposta di @ JonathanFrech.


4

Mathematica, 180 156 141 153 138 136 104 byte

MemberQ[#|Table[""<>Part[Join@@#,p],{x,1##4},{y,1##4},{p,FindPath[GridGraph@{##4},x,y,#3,All]}],#2,All]&

Esempio di input

[{{"1","1","1","1","1"},{"0","0","0","0","0"}},"10011001",8,5,2]

Spiegazione

  1. GridGraph@{##4}è un Graphoggetto per una griglia di vertici con vertici adiacenti collegati da bordi, con dimensioni {##4}- ovvero, {#4,#5}o {width,height}.
  2. Esaminiamo tutti i vertici iniziali x(numerati 1a 1##4 = width*height), tutti i vertici finali ye tutti i percorsi pdi lunghezza al massimo #3da xa y.
  3. Per ciascuno di questi percorsi, ""<>Part[Join@@#,p]estrae i caratteri corrispondenti della matrice e li inserisce in una stringa.
  4. Includiamo anche la matrice stessa, i cui caratteri sono tutte le stringhe di lunghezza 1 che possono essere trovate in essa.
  5. Vediamo se una di queste stringhe corrisponde s, cercando a tutti i livelli perché questo è un elenco molto multidimensionale che abbiamo creato.

Nota: la sostituzione #3di {#3-1}in FindPath, in modo da trovare solo percorsi della lunghezza corretta, rappresenta un enorme miglioramento in termini di velocità, ma costa 4 byte in più.


-24 byte: prendendo le dimensioni delle cose come input

-15 byte: usando StringParte StringJoincorrettamente

+12 byte: correzione del caso lunghezza-1

-15 byte: ...

-2 byte: prendendo la dimensione della matrice come input come un array

-32 byte: usare Tableper scorrere il percorso ci permette di evitare di usarlo Function, e usare MemberQ[...,s,All]ci permette di incollare la matrice sul tavolo quando abbiamo a che fare con serpenti di lunghezza 1.


3

C # (.NET Core) , 346 341 336 302 297 byte

(m,h,w,s,l)=>{for(int y=0;y<h;y++)for(int x=0;x<w;x++)if(N(x,y,l-1))return 0<1;return 1<0;bool N(int x,int y,int p){if(p<0)return 0<1;if(y<0|x<0|y==h|x==w||m[y,x]>1||s[p]!=m[y,x])return 1<0;int g=m[y,x];m[y,x]=2;if(N(x,y-1,--p)||N(x-1,y,p)||N(x,y+1,p)||N(x+1,y,p))return 0<1;m[y,x]=g;return 1<0;}}

Provalo online!

5 byte salvati giocando a golf p incremento

5 byte salvati prendendo la lunghezza del serpente e iniziando dalla sua coda, e rimuovendo uno spazio non necessario

34 byte salvati leggendo correttamente la sfida e vedendo che posso prendere l'altezza e la larghezza della matrice

5 byte salvati, il caso di test a singolo elemento non è riuscito e la correzione è stata utile

Ungolfed

(m,h,w,s,l)=>{
    // Go through every potential starting point
    for(int y=0; y<h; y++)
        for(int x=0; x<w; x++)
            if(N(x,y,l-1)) // start the recursive steps
                return 0<1; // return true if N returns true, otherwise check the next element

    return 1<0; // return false as the snake doesn't fit into the matrix

    // C#7 local function in a Func
    bool N(int x, int y, int p)
    {
        // if there is no more snake to fit return true
        if(p<0)
            return 0<1;

        // if m element has part of the snake or 
        // snake part doesn't match matrix element then return false
        if(y<0 | x<0 | y==h | x==w || m[y,x]>1 || s[p] != m[y,x])
            return 1<0;

        // hold the current matrix element
        int g=m[y,x];
        // set the current matrix element to 2 to indicate it has a part of the snake
        m[y,x]=2;

        // check each of the four neighbours and recurse down that neighbour 
        // except if they are outside the matrix
        if(N(x,y-1,--p) ||
           N(x-1,y,p) ||
           N(x,y+1,p) ||
           N(x+1,y,p))
               return 0<1; // return true if remainder of the snake fits into the matrix

        // if snake doesn't fit then set the matrix element as not having part of the snake
        m[y,x]=g;
        // return false to indicate this neighbour direction doesn't fit the snake
        return 1<0; 
    }
}

Un inizio di golf sarebbe rimuovere tutti gli spazi bianchi non necessari ...
Jonathan Frech,

if(...)return true;-> return ...;.
Jonathan Frech,

@JonathanFrech D'accordo, ma l'ho lasciato così per consentire ad altri di leggerlo un po 'più facilmente fino a quando non avrò la possibilità di tornarci (domani).
Ayb4btu,

@JonathanFrech Non funziona, b[y,x]a un certo punto deve essere ripristinato. (Mi dispiace anche per aver sbagliato a scrivere il tuo nome nella mia risposta.)
Neil

Intendevo if(N(x,y,0)>0)return 0<1;; la prima apparizione di return.
Jonathan Frech,

1

Kotlin , 413 byte

var x:(Array<Array<Char>>,String)->Boolean={b,s->fun f(s:String,x:Int,y:Int):Boolean{if(b[x][y]!=s[0])
return 0>1
if(s.length<2)
return 1>0
val v=b[x][y]
b[x][y]='Z'
try{return(-1..1).map{x+it}.flatMap{t->(-1..1).map{y+it}.map{t to it}}.filter{(X,Y)->(x-X)*(x-X)+(y-Y)*(y-Y)==1&&X in b.indices&&Y in b[0].indices&&f(s.substring(1),X,Y)}.any()}finally{b[x][y]=v}}
b.indices.any{x->(0..b[0].size-1).any{f(s,x,it)}}}

Abbellire

var x: (Array<Array<Char>>, String) -> Boolean = { b, s ->
    fun f(s: String, x: Int, y: Int): Boolean {
        if (b[x][y] != s[0])
            return 0 > 1
        if (s.length < 2)
            return 1 > 0
        val v = b[x][y]
        b[x][y] = 'Z'
        try {
            return (-1..1).map{ x + it }
                    .flatMap { t -> (-1..1).map{y+it}.map { t to it } }
                    .filter { (X, Y) ->
                        (x - X)*(x - X) + (y - Y)*(y - Y) == 1 &&
                                X in b.indices && Y in b[0].indices &&
                                f(s.substring(1), X, Y) }
                    .any()
        } finally {
            b[x][y] = v
        }
    }
    b.indices.any { x -> (0..b[0].size - 1).any { f(s, x, it) } }
}

Test

var x:(Array<Array<Char>>,String)->Boolean={b,s->fun f(s:String,x:Int,y:Int):Boolean{if(b[x][y]!=s[0])
return 0>1
if(s.length<2)
return 1>0
val v=b[x][y]
b[x][y]='Z'
try{return(-1..1).map{x+it}.flatMap{t->(-1..1).map{y+it}.map{t to it}}.filter{(X,Y)->(x-X)*(x-X)+(y-Y)*(y-Y)==1&&X in b.indices&&Y in b[0].indices&&f(s.substring(1),X,Y)}.any()}finally{b[x][y]=v}}
b.indices.any{x->(0..b[0].size-1).any{f(s,x,it)}}}

data class Test(val board: String, val snake: String, val output: Boolean)

val tests = listOf(
        Test("""01010
            |10101
            |01010
            |10101
            |01010""", "0101010101010101010101010", true),
        Test("""01110
            |01100
            |10010
            |10110
            |01101""", "011111000110100", true),
        Test("""0""", "0", true),
        Test("""10
            |01""", "1010", true),
        Test("""100
            |010
            |001""", "100010001", true),
        Test("""00000
            |00000
            |00000
            |00000
            |00000""", "1", false),
        Test("""10101
            |01010
            |10101
            |01010
            |10101""", "11", false),
        Test("""100
            |010
            |001""", "111", false),
        Test("""10001
            |01010
            |00100
            |01010
            |10001""", "1000100010001000101010100", false)
)

fun main(args: Array<String>) {
    tests.filter {(board, snake, expected) ->
        val boardR = board.trimMargin().lines().map { it.toCharArray().toTypedArray() }.toTypedArray()
        val result = x(boardR, snake)
        result != expected
    }.forEach { throw AssertionError(it) }
    println("Test Passed")
}
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.