マトリックス内のヘビを見つける


32

チャレンジ

バイナリマトリックスとバイナリ文字列が与えられたら、そのバイナリ文字列がマトリックス内の任意のポイントから始まり、その後の任意のポイントで任意の方向に移動してバイナリ文字列を形成できるかどうかを判断します。つまり、文字列はマトリックス内で折りたたまれていますか?

文字列は90度または180度(エッジ接続、マンハッタン距離1)でのみ折りたたむことができ、どの点でも重なることはできません。

次の例を見てみましょう。

Matrix:

010101
111011
011010
011011

Snake: 0111111100101

これは真実のテストケースです。次の位置でヘビが折りたたまれているのがわかります。

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

ルール

  • 標準的な抜け穴が適用されます
  • 必要に応じて、文字列の長さと行列の幅と高さを入力として使用できます
  • バイナリ行列とバイナリ文字列を、複数行の文字列/文字列の配列/改行結合文字列/その他の結合文字列と文字列として取ることができます
  • 複数の引数の代わりに、次元をフラット配列として取ることができます
  • プログラムは、長さ10までの文字列を含む5 x 5マトリックスで1分以内に終了する必要があります

制限事項

  • 行列は必ずしも正方形ではありません
  • 文字列は空ではありません
  • 文字列は長さ1にすることができます
  • 文字列には、使用可能な数よりも多くの正方形は含まれません(つまり、 len(string) <= width(matrix) * height(matrix)

テストケース

真実の

01010
10101
01010
10101
01010

0101010101010101010101010



01110
01100
10010
10110
01101

011111000110100



0

0



10
01

1010



100
010
001

100010001

偽物

00000
00000
00000
00000
00000

1



10101
01010
10101
01010
10101

11



100
010
001

111



10001
01010
00100
01010
10001

1000100010001000101010100


4
または:Binary Boggle!また、さらにいくつかのテストケースを追加できますか?
ジョナ

1
この文脈で、フラット、シャープ、ラウンドとはどういう意味ですか?正方形ではないということは、幅と高さが等しくないか、配列がギザギザになる可能性があることを意味しますか?
Tahg

地球上で丸い配列は何
ですか

回答:


13

Pythonの2275 271 264 249バイト

  • に置き換え-1て4バイトを保存しましたH1つのスライス操作([:])に削除すること。
  • Halvard Hummelのおかげで7バイト節約されました。さらに別のスライシング操作を削除し([:])、複数ターゲットの割り当てを使用して訪問済みエントリに値v not in "01"S=S[1:];M[y][x]=H;-> S=M[y][x]=S[1:];)を与え、三項if / elseから単純な論理的または(any(...)if S else 1 ->not S or any(...))にます。
  • truthyfalseyの定義をいくぶん拡張すると、この257バイトの長さのソリューションを許可できます。例外を発生させます(ZeroDivisionErrorヘビが見つかった場合は)が発生し、ヘビが見つから[]ない場合は空のリスト()が返されます。これは2つの異なる動作です。
  • user202729のおかげで14バイト節約されました。ゴルフ2つの配列の深いコピー
  • バイトを保存しました。〜not S orまでゴルフ。S<[1]orS==[]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])

オンラインでお試しください!

説明

マトリックスを文字列("0"または"1")の2次元リスト、スネークを1次元リスト、マトリックスの次元を2つの整数として受け取るLambda関数。
ラムダ関数は、ヘビの最初の要素に一致するエントリをマトリックスで検索します。見つかったすべての一致に対して、それは呼び出しますH、マトリックスの深いコピー、ヘビのコピーなし、マトリックスの次元、および一致の位置を。

ときHに呼び出され、それが削除されますS"最初のエントリとセット以外の何かに指定された位置の行列エントリを"0", "1"。場合はS「長さがゼロである、それが返されますTrue。再帰的に呼び出されるため、ヘビはマトリックスのどこかに見つかりました。
場合Sの最初の要素とその位置に要素「の長さがゼロで、その位置が行列にある場合、それは、東西南北の方向を介してテストループ、マトリックスを比較」Sが一致する場合- -自分自身を再帰的に呼び出し、。
Hの戻り値はスタックフレームに集められ、少なくとも1つの関数がスネークを検出したかどうかを常にチェックします。

フォーマットされた出力

私はプログラムを強化して、ヘビのスリッシャーのパスも出力します(ある場合)。質問と同じASCII出力デザインを使用します。TIOリンク



1
@HalvardHummelありがとう。特に、余分なスライス操作を見つけるために。
ジョナサンフレッチ

@ user202729あなたはm[:]for〜> と思いますm*1forか?うまくいくかもしれない。
ジョナサンフレッチ

@ user202729ありがとう、リンクされたヒントは、これが深いコピーが必要だと思うので機能しました。
ジョナサンフレッチ

9

JavaScript(ES6)、138 134

@Neilのものとそれほど違いはありませんが、他に何があるでしょうか?

入力:複数行の文字列、バイナリ文字列、幅(改行をカウントしない)としての行列

注意:再帰関数のロジックは、r数バイトを節約するために幾分反転されています

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

少ないゴルフ

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

テスト

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バイト

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

行列を改行区切りの文字列、スネークを文字列、幅(整数)として受け取ります。@JonathanFrechの回答に大まかに基づいています。


4

Mathematica、180 156 141 153 138 136 104バイト

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

入力例

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

説明

  1. GridGraph@{##4}は、Graphエッジで接続された隣接する頂点を持つ次元のグリッドのオブジェクトです。{##4}つまり、{#4,#5}または{width,height}です。
  2. 我々反復にわたる全ての出発頂点x(番号11##4 = width*height)、全て終了頂点y、及び全てのパスp最大でも長さの#3よりxy
  3. そのようなパスごと""<>Part[Join@@#,p]に、マトリックスの対応する文字を抽出し、文字列に入れます。
  4. また、マトリックス自体も含めます。その文字はすべて、その中にある長さ1の文字列です。
  5. これらの文字列のいずれかが一致するかどうかを確認sします。これは、作成した非常に多次元のリストであるため、すべてのレベルで検索します。

注:交換#3によって{#3-1}FindPath、私たちだけ正確に正しい長さのパスを見つけること、速度の点で大幅に改善される-しかし、コストが4以上のバイト。


-24バイト:物の次元を入力として取得

-15バイト:使用するStringPartStringJoin、正しく

+12バイト:長さ1のケースを修正

-15バイト:...

-2バイト:行列のサイズを配列として入力として取得

-32バイト:Tableパスを反復処理するために使用するFunctionと、の使用を避けることができ、使用MemberQ[...,s,All]すると、長さ1のヘビを処理するときに、マトリックスをテーブルに並べるだけで済みます。


3

C#(.NET Core)346 341 336 302 297バイト

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

オンラインでお試しください!

p増分をゴルフすることで5バイト節約

ヘビの長さを取り、尾から始めて、不要なスペースを削除することで5バイト節約

チャレンジを適切に読み取り、マトリックスの高さと幅を取得できることを確認することで34バイト節約

5バイトが保存され、単一要素のテストケースは失敗し、修正は有益でした

非ゴルフ

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

ゴルフのスタートは、すべての不要な空白...削除するだろう
ジョナサンFRECH

if(...)return true;-> return ...;
ジョナサンフレッチ

@JonathanFrech同意しましたが、私はそれを残して、他の人がそれを取り戻す機会が得られるまで(明日になるまで)もう少し簡単に読めるようにしました。
Ayb4btu

@JonathanFrechは機能しませんb[y,x]。ある時点でリセットする必要があります。(私の答えであなたの名前のつづりを間違えて申し訳ありません。)
ニール

私が意味したif(N(x,y,0)>0)return 0<1;; の最初の登場return
ジョナサンフレッチ

1

Kotlin、413バイト

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

美化

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

テスト

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")
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.