Perl 5, -p0
105 101 96 93 90 89 bytes
Uses b
instead of 1
in the input.
Make sure the matrix on STDIN is terminated with a newline
#!/usr/bin/perl -p0
s%b%$_="$`z$'";s:|.:/
/>s#(\pL)(.{@{-}}|)(?!\1)(\pL)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg
Try it online!
Uses 3 levels of substitution!
This 87 byte version is both in input and output format easier to interpret, but is not competing since it uses 3 different characters in the output:
#!/usr/bin/perl -0p
s%b%$_="$`z$'";s:|.:/
/>s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg
Try it online!
It's easy to save another byte (the regex s
modifier) in both versions by using some different (non-alphanumeric) character as row terminator (instead of newline), but that makes the input quite unreadable again.
How it works
Consider the substitution
s#(\w)(.{columns}|)(?!1)(\w)#c$2c#s
This will find two letters that are different and next to each other horizontally or vertically and replace them by c
. In a maze whose paths consist of entirely the letter b
nothing will happen since the letters are the same, but as soon as one of the letters is replaced by another one (e.g. z
) that letter and a neighbor will be replaced by c
and repeated application is a flood-fill of the connected component with c
from seed z
.
In this case I however don't want a complete flood-fill. I want to fill only one of the arms neighboring z
, so after the first step I want the z
gone. That already works with the c$2c
replacement, but I later want to restart a flood-fill along another arm starting from the same point and I don't know which of the c
s was originally z
anymore. So instead I use
s#(\w)(.{columns}|)(?!\1)(\w)#$&|a.$2.a#se
b | a
is c
, b | c
is c
and z | a
is {
. So in a maze with paths made up of b
and a seed z
in the first step b
will get replaced by c
and z
will get replaced by {
which is not a letter and does not match \w
and so will not cause further fills. The c
however will keep a further flood-fill going and one neighbor arm of the seed gets filled. E.g. starting from
b c
b c
bbzbb becomes bb{bb
b b
b b
I can then replace all c by some non letter (e.g. -
) and replace {
by z
again to restart the flood-fill:
- -
- -
bbzbb becomes cc{bb
b b
b b
and repeat this process until all neighbors of the seed have been converted. If I then once more replace {
by z
and flood-fill:
- -
- -
--z-- stays --z--
- -
- -
The z
remains behind at the end because there is no neighbor to do a transformation with.
That makes clear what happens in the following code fragment:
/\n/ >
Find the first newline. The starting offset is now in @-
s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se
The regex discussed above with @{-}
as number of columns (since plain @-
confuses the perl parser and doesn't properly substitute)
&&
The /\n/
always succeeds and the substitution is true as long as we can still flood-fill. So the part after &&
is executed if the flood-fill of one arm is done. If not the left side evaluates to an empty string
y/{c/z / > 0
Restart the flood-fill and return 1 if the previous flood-fill did anything. Else return the empty string. This whole piece of code is wrapped inside
s:|.: code :seg
So if this is executed on a starting string $_
with a z
at the seed position the piece of code inside will be executed many times mostly returning nothing but a 1
every time a neighbor arm gets flood-filled. Effectively $_
gets destroyed and replaced by as many 1
s as there are connected components attached to z
. Notice that the loop needs to be executed up to sum of component sizes + number of arms times but that is OK since it will "number of characters including newlines * 2 + 1" times.
The maze gets disconnected if there are no 1
's (empty string, an isolated vertex) or if there are more than 1 arms (more than 2 1
s). This can be checked using the regex /\B/
(this gives 0
instead of 1
on older perl versions. It's arguable which one is wrong). Unfortunately if it doesn't match this will give an empty string instead of 0
. However the s:|.: code :seg
was designed to always return an odd number so by doing an &
with /\B/
this will give 0
or 1
.
All that is left is walking the whole input array and at each walkable position seed with z
and count connected arms. That is easily done with:
s%b%$_="$`z$'"; code %eg
The only problem is that in the non-walkable positions the old value is retained. Since we need 0
s there that means the original input array must have 0
in the non walkable positions and 0
matches \w
in the original substitution and would trigger flood-fills. That's why I use \pL
instead (only match letters).
so, find all bridges in all subgraphs – HyperNeutrino – 2018-03-07T21:27:12.823
1I think that the challenge would benefit from a step-by-step example for a smaller matrix. – Mr. Xcoder – 2018-03-07T21:28:25.653
@HyperNeutrino yes, except I tried to use mainstream graph-theoretic terminology – ngn – 2018-03-07T21:29:58.207
ok. (..pretty sure they are called bridges formally but idk lol) – HyperNeutrino – 2018-03-07T21:33:19.707
@Mr.Xcoder There are multiple possible approaches for solving this. I prefer not to describe any particular algorithm. – ngn – 2018-03-07T21:34:54.790
1@HyperNeutrino a bridge is something different - it's an edge (not a vertex) whose removal increases the number of connected components – ngn – 2018-03-07T21:38:27.553
1@HyperNeutrino also, a subgraph is not quite the same as a connected component – ngn – 2018-03-07T21:40:06.537
oh right, it does slightly differ. nvm – HyperNeutrino – 2018-03-07T21:52:44.620
@HyperNeutrino "bridge"? I think it should be articulation point. – user202729 – 2018-03-08T04:37:55.843
Can we choose other characters than
01
in input and/or output ? – Ton Hospel – 2018-03-08T09:35:01.640@TonHospel sure, you can also use 01 as numbers or true/false if you language has a boolean type, whichever is most convenient – ngn – 2018-03-08T12:25:35.130
Isn’t a cutpoint usually defined to be a vertex whose deletion increases the number of components, not just changes the number? Your definition counts isolated vertices as cutpoints, but this one doesn’t.
– Not a tree – 2018-03-08T17:07:31.7731@Notatree You're right. I made a mistake. It's too late to fix it now but I hope it won't spoil the fun. – ngn – 2018-03-08T17:55:17.990
I originally lost 5 bytes to fix this behaviour for isolated vertices but by now I optimzed it so much that it would be longer to not count them :-) – Ton Hospel – 2018-03-08T18:34:47.707
@TonHospel huh, i thought it would be just a matter of < vs != – ngn – 2018-03-08T18:57:13.423
@ngn a test case about isolated vertices? 000,010,000 -> 000,010,000 ? – edc65 – 2018-03-09T10:42:32.810
@edc65 the third test case has some of those – ngn – 2018-03-09T12:05:27.293