Schlangen in einer Matrix finden


32

Herausforderung

Bestimmen Sie anhand einer Binärmatrix und einer Binärzeichenfolge, ob diese Binärzeichenfolge an einem beliebigen Punkt in der Matrix gefunden werden kann und sich an einem beliebigen nachfolgenden Punkt in eine beliebige Richtung bewegt, um die Binärzeichenfolge zu bilden. Das heißt, kann die Zeichenfolge jedoch innerhalb der Matrix zusammengefaltet gefunden werden?

Die Saite kann nur um 90 Grad oder 180 Grad gefaltet werden (Kantenverbindungen; Manhattan Distance 1) und darf sich an keiner Stelle überlappen.

Beispiel

Nehmen wir das folgende Beispiel:

Matrix:

010101
111011
011010
011011

Snake: 0111111100101

Dies ist ein wahrer Testfall. Wir können die gefaltete Schlange in der folgenden Position sehen:

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

Regeln

  • Es gelten Standardlücken
  • Sie können die Länge der Zeichenfolge und die Breite und Höhe der Matrix als Eingabe verwenden, wenn Sie möchten
  • Sie können die Binärmatrix und die Binärzeichenfolge als eine mehrzeilige Zeichenfolge / ein Array von Zeichenfolgen / eine durch Zeilenumbruch verbundene Zeichenfolge / eine beliebige andere verbundene Zeichenfolge und eine Zeichenfolge annehmen
  • Sie können die Dimensionen als flaches Array anstelle mehrerer Argumente verwenden
  • Ihr Programm muss für jede 5 x 5-Matrix mit einer Zeichenfolge von bis zu 10 in weniger als einer Minute abgeschlossen sein

Einschränkungen

  • Die Matrix ist nicht unbedingt quadratisch
  • Die Zeichenfolge ist nicht leer
  • Die Zeichenfolge kann die Länge 1 haben
  • Die Zeichenfolge enthält nicht mehr Quadrate als verfügbar (d. H. len(string) <= width(matrix) * height(matrix)

Testfälle

Wahrheit

01010
10101
01010
10101
01010

0101010101010101010101010



01110
01100
10010
10110
01101

011111000110100



0

0



10
01

1010



100
010
001

100010001

Falsch

00000
00000
00000
00000
00000

1



10101
01010
10101
01010
10101

11



100
010
001

111



10001
01010
00100
01010
10001

1000100010001000101010100


4
Oder: Binary Boggle! Können Sie noch einige Testfälle hinzufügen?
Jonah

1
Was bedeuten flach, scharf und rund in diesem Zusammenhang? Bedeutet das nicht, dass Breite und Höhe möglicherweise nicht gleich sind oder dass das Array gezackt sein kann?
Tahg

Was in aller Welt ist eine runde Reihe
Conor O'Brien

Antworten:


13

Python 2 , 275 271 264 249 Bytes

  • Durch Ersetzen -1durch Hund Entfernen eines Slicing-Vorgangs ( [:]) wurden vier Bytes gespart .
  • Dank Halvard Hummel sieben Bytes gespart ; Entfernen einer weiteren Slicing-Operation ( [:]), Zuweisen mehrerer Ziele, um einem besuchten Eintrag einen Wert zuzuweisen v not in "01"( S=S[1:];M[y][x]=H;-> S=M[y][x]=S[1:];) und von einem ternären if / else zu einem einfachen logischen oder ( any(...)if S else 1-> not S or any(...)) zu wechseln .
  • Wenn Sie Ihre Definition von Wahrhaftigkeit und Falschheit etwas erweitern , können Sie diese 257 Byte lange Lösung zulassen . Es wird eine Ausnahme ( ZeroDivisionError) ausgelöst, wenn die Schlange gefunden wird, und eine leere Liste ( []) zurückgegeben, wenn keine Schlange gefunden werden kann, die zwei unterschiedliche Verhaltensweisen aufweist.
  • Vierzehn Bytes dank user202729 gespeichert ; Golf zwei Array Deep Kopien
  • Ein Byte gespeichert; Golf not S orzu 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])

Probieren Sie es online!

Erläuterung

Lambda-Funktion, die die Matrix als zweidimensionale Liste von Zeichenfolgen (entweder "0"oder "1"), die Schlange als eindimensionale Liste und die Matrixdimensionen als zwei ganze Zahlen aufnimmt.
Die Lambda-Funktion durchsucht die Matrix nach Einträgen, die mit dem ersten Element der Schlange übereinstimmen. Für jede gefundene Übereinstimmung wird Heine tiefe Kopie der Matrix, keine Kopie der Schlange, die Matrixdimensionen und die Position der Übereinstimmung abgerufen.

Wenn Haufgerufen wird, wird Sder erste Eintrag entfernt und der Matrixeintrag der angegebenen Position auf etwas anderes als gesetzt "0", "1". Wenn S'length gleich Null ist, wird zurückgegeben True. wie es sich rekursiv nennt, wurde die Schlange irgendwo in der Matrix gefunden.
Wenn S'length ungleich Null ist, durchläuft es die vier Himmelsrichtungen, prüft, ob diese Position in der Matrix enthalten ist, vergleicht das Matrixelement an dieser Position mit dem ersten Element von Sund ruft sich - falls es übereinstimmt - rekursiv auf.
HDie Rückgabewerte von werden über die Stapelrahmen übertragen, wobei immer geprüft wird, ob mindestens eine Funktion eine mögliche Schlange gefunden hat.

Formatierte Ausgabe

Ich habe mein Programm erweitert, um auch den Pfad auszugeben, den die Schlange schlängelt (falls es einen gibt). Es wird das gleiche ASCII-Ausgabedesign wie für die Frage verwendet. TIO-Link .



1
@HalvardHummel Danke; speziell zum Erkennen des überflüssigen Aufschneidevorgangs.
Jonathan Frech

@ user202729 Du denkst m[:]for~> m*1for? Könnte funktionieren.
Jonathan Frech

@ user202729 Danke, der verlinkte Tipp hat funktioniert, da ich denke, dass dies eine tiefe Kopie benötigt.
Jonathan Frech

9

JavaScript (ES6), 138 134

Nicht so verschieden von @ Neil's, aber was könnte es sonst sein?

Eingabe: Matrix als mehrzeilige Zeichenfolge, binäre Zeichenfolge, Breite (ohne Zeilenvorschub)

Anmerkung: Die Logik in der rekursiven Funktion rist etwas invertiert, um ein paar Bytes zu sparen

(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))

Weniger golfen

(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))
)

Prüfung

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)))

Nimmt die Matrix als durch Zeilenumbrüche getrennte Zeichenfolge, die Schlange als Zeichenfolge und die Breite (als Ganzzahl). Lose basierend auf @ JonathanFrechs Antwort.


4

Mathematica, 180 156 141 153 138 136 104 Bytes

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

Beispiel Eingabe

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

Erläuterung

  1. GridGraph@{##4}ist ein GraphObjekt für ein Gitter von Scheitelpunkten mit benachbarten Scheitelpunkten, die durch Kanten verbunden sind, mit Dimensionen {##4}- das heißt, {#4,#5}oder {width,height}.
  2. Wir iterieren über alle Startscheitelpunkte x(nummeriert 1bis 1##4 = width*height), alle Endscheitelpunkte yund phöchstens alle Längenpfade #3von xbis y.
  3. Extrahiert für jeden solchen Pfad ""<>Part[Join@@#,p]die entsprechenden Zeichen der Matrix und fügt sie in eine Zeichenfolge ein.
  4. Wir schließen auch die Matrix selbst ein, deren Zeichen alle Zeichenfolgen der Länge 1 sind, die darin zu finden sind.
  5. Wir sehen, ob eine dieser Zeichenfolgen übereinstimmt sund suchen auf allen Ebenen, da dies eine sehr mehrdimensionale Liste ist, die wir erstellt haben.

Hinweis: Das Ersetzen #3durch {#3-1}in FindPath, damit wir nur Pfade mit genau der richtigen Länge finden, ist eine enorme Geschwindigkeitsverbesserung - kostet aber 4 weitere Bytes.


-24 Bytes: Dimensionen von Dingen als Eingabe nehmen

-15 Bytes: Verwenden StringPartund StringJoinrichtig

+12 Bytes: Behebung des Länge-1-Falls

-15 Bytes: ...

-2 Bytes: Die Größe der Matrix wird als Eingabe für ein Array verwendet

-32 Bytes: TableWenn Sie verwenden , um über den Pfad zu iterieren, vermeiden Sie die Verwendung Function, und MemberQ[...,s,All]wenn Sie verwenden, haften Sie die Matrix beim Umgang mit Schlangen der Länge 1 auf der Tabelle.


3

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

(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;}}

Probieren Sie es online!

5 Bytes gespart durch Golfen des pInkrements

5 Bytes werden eingespart, indem die Schlangenlänge aufgenommen und am Ende begonnen wird und unnötiger Speicherplatz entfernt wird

34 Bytes wurden gespart, indem die Herausforderung richtig gelesen wurde und ich die Höhe und Breite der Matrix einschätzen konnte

5 Bytes gespart, der Einzelelement-Testfall fehlgeschlagen und die Korrektur von Vorteil

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; 
    }
}

Ein Start zum Golfen wäre, alle unnötigen Leerzeichen zu entfernen ...
Jonathan Frech

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

@JonathanFrech Einverstanden, aber ich habe es so belassen, damit andere es leichter lesen können, bis ich die Möglichkeit habe, darauf zurückzukommen (irgendwann morgen).
Ayb4btu

@ JonathanFrech Funktioniert nicht, b[y,x]muss irgendwann zurückgesetzt werden. (Tut mir auch leid, dass du deinen Namen in meiner Antwort falsch geschrieben hast.)
Neil,

Ich meinte if(N(x,y,0)>0)return 0<1;; der erste Auftritt von return.
Jonathan Frech

1

Kotlin , 413 Bytes

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)}}}

Verschönert

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) } }
}

Prüfung

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")
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.