| Bytes | Lang | Time | Link |
|---|---|---|---|
| 131 | JavaScript ES8 | 241205T100557Z | Arnauld |
| 156 | Python 3 | 241204T113839Z | tsh |
| 128 | Ruby | 241204T091626Z | Doorknob |
| 341 | Python3 | 241204T173738Z | Ajax1234 |
| 040 | 05AB1E | 241204T094734Z | Kevin Cr |
| 101 | Charcoal | 241204T092831Z | Neil |
JavaScript (ES8), 131 bytes
-3 bytes and a bug fixed thanks to @l4m2
Expects (array)(string), where array is of the form [ [ "x","y",n ], ... ]. Returns a string.
The symbols used are non-zero decimal digits.
a=>m=g=s=>a.map(g[s]=([x,y,n])=>(h=(p,q)=>g[S=s.replace(p,[q])]?S>+m?0:m=S:g(S))(/(.)\1/)&h(x=x.padEnd(n,y+x),y+=x/10|0)&h(y,x))&&m
Commented
a => // outer function taking the array a[]
m = // m = best answer so far, initially NaN'ish
g = s => // g is a recursive function taking the string s
a.map(g[s] = // eventually mark g[s] as encountered
([x, y, n]) => // for each triplet (x, y, n) in a[]:
( h = (p, q) => // h is a helper function taking (p, q):
g[ // test g[S] ...
S = s.replace // ... where S is obtained by replacing in s
(p, [q]) // the first occurrence of p with q
] ? // if g[S] is defined:
S > +m ? // if S is greater than m (as integers):
0 // do nothing
: // else:
m = S // update m to S
: // else:
g(S) // do a recursive call with S
)(/(.)\1/) & // invoke h to remove a doubled symbol
h( // invoke h to replace:
x = x.padEnd // the string "xyxy.." of length n (n>1)
(n, y + x), //
y += x / 10 | 0 // with the string "yxyx.." of length n
) & //
h(y, x) // invoke h to replace "yxyx.." with "xyxy.."
) // end of map()
&& m // return the final answer
Python 3, 156 bytes
f=lambda r,*a:f(r,*T)if{*a}^(T:={k.replace(x[:n]or y,x[1:n+1])for k in a for y,n in r+[(c+c,0)for c in k]for x in[y*n,y[1]+y*n]})else min(sorted(T),key=len)
-16 bytes by Jonathan Allan
Ruby, 172 164 157 133 131 128 bytes
->r,*a{(r*a[0].size**9).map{|x,n|x*=n;a.map{a+=[_1.sub(/(.)\1/,'').sub(q=x[0,n],w=x[1,n]),_1.sub(w,q)]}}
a.min_by{[_1.size,_1]}}
It is hard to exaggerate how comically slow this is. It would use about \$2^{10,000,000}\$ gigabytes of memory for the smallest test case. But you can try it here with some slight modifications so it can run on a sensible machine.
-24 from stealing lots of improvements from tsh's python answer, thanks! Will update the explanation below when I have time, but for now it refers to the previous version:
->s,r{a=[s]
(r*s.size**9).map{|x,n|x*=n;a.map{|s|s.sub! /(.)\1/,'';a+=s.gsub(/#{x[0...n]+?|+x[1..n]}/).map{$`+$&.tr(x[1..2],x)+$'}}}
a.min_by{|s|[s.size,s]}}
The general idea is to initialize an array containing just the input,
then repeatedly apply all* possible transformations
to all elements of the array,
and append the new results.
If it takes \$n\$ transformations to get to the fully reduced form,
then as long as we have done this at least \$n\$ times,
the array will contain the answer somewhere,
and we can take the min_by length (tiebreaking alphabetically) to get it.
Of course, since we never throw away the previous results, every single string we have ever seen gets processed again on each iteration. This means the size of the array grows exponentially, which makes this solution totally impractical for any nontrivial input. But that's okay, this is code golf.
The first line (initialize the array) and the last line (take the winner) are self-explanatory -- the meat of the solution is line 2. Let's start with the regex:
x*=n; [...] /#{x[0...n]+?|+x[1..n]}/
x is the two characters in a braid and n is the reducible braid length.
We build this pattern
(by modifying x in place!)
that matches any possible substring of the two characters in x
that can be transformed.
Note that if there is a braid longer than the corresponding relation,
there are several overlapping matches,
but we only get some.
This is fine, though:
making the first substitution yields an adjacent pair of same symbols,
which after removal has the net effect of shortening the braid
until it is at most as long as the corresponding relation.
.gsub( - ).map{$`+$&.tr(x[1..2],x)+$'}
This is a pretty neat bit of Ruby flair.
We call gsub,
the global regex replace function,
on that regex,
but decline to give a replacement.
This makes gsub instead return an enumerator over each match.
We ignore the value of the enumerator
and instead refer to some magic globals:
$` is the string before the match,
$& is the match,
and $' is the string after the match.
So we map over each match,
and for each one return the original string
with the matched substring replaced with $&.tr(x[1..2],x),
swapping the relevant characters.
x used to be two characters,
but x[1..2] now exists because we modified x in place in the previous step,
which is delightfully gross.
a.map{|s|s.sub! /(.)\1/,'';a+=s.gsub - }
The main body of the loop first deletes a single adjacent pair, if any,
in each string.
We unfortunately have to do this outside the gsub
because the input might be something like aabc with no relevant relations,
where the only thing to do is delete the aa
(and all the gsub.maps return an empty array).
Obviously,
this is worse than writing s.gsub! instead,
to delete all adjacent pairs instead of only one.
But that's okay,
this is code golf.
(r*s.size**9).map{|x,n| - }
Finally, we do that whole thing \$|s|^9\$ times. Why? Well, we need an upper bound on how many steps it might take to reduce. I'm pretty sure \$|s|^2\$ is actually such a bound: there can be at most \$|s|\$ deletion steps, and between each deletion step there can be at most \$|s|-1\$ useful braid-swapping steps (because a step is only useful if it brings the two symbols involved in the next deletion closer together). I put a 9 instead of a 2 just to be safe. Naturally, this makes the runtime even more hilariously impractical. But that's okay, this is code golf.
Python3, 341 bytes
R=range
def f(a,b):
q,s,S=[a],[],[a]
for A in q:
s+=[A]
for j,k in b:
u,U,T='','',[]
for i in R(k):u+=j[i%2];U+=j[j[i%2]==j[0]]
for i in R(len(A)-1):
if A[i]==A[i+1]:T+=[A[:i]+A[i+2:]]
if(V:=A[i:i+len(u)])in[u,U]:T+=[A[:i]+[u,U][V==u]+A[i+len(u):]]
for i in T:
if i not in S:q+=[i];S+=[i]
return min(s,key=len)
05AB1E, 40 bytes
ε`∍ÐÙ‡‚}Dí«sÙSºõδ‚«U¸ΔDεVXεYs`:}}«˜êé}н
Inputs in reversed order, with the braid relations as a list of \$[string,length]\$-pairs.
Try it online or verify all test cases.
Explanation:
Step 1: Create a list of all possible replacements:
ε # Map over the (implicit) first input of braid relations:
` # Pop the pair, and push string and length separately
∍ # Extend the string to the specified length
Ð # Triplicate this string
Ù # Uniquify the top copy
 # Bifurcate it; short for Duplicate & Reverse copy
‡ # Transliterate (e.g. "ababa","ab","ba" becomes "babab")
‚ # Pair the two 'mirrored' strings together
}D # After the map: duplicate the list of pairs
í # Reverse each pair in the copy
« # Merge the two lists together
s # Swap to get the second (implicit) input-string
Ù # Uniquify its characters
S # Convert the string to a list of characters
º # Mirror/double each character in the list
δ # Map over each double letter string:
õ ‚ # Pair it with an empty string
« # Merge the two lists together
U # Pop and store this list of replacements in variable `X`
Try just this first online (without U).
Step 2: Keep expanding the (sorted) list as long as there are replacements possible:
¸ # Wrap the second (implicit) input-string into a singleton list
Δ # Loop until the list no longer changes:
D # Duplicate the current list
ε # Map over each string:
V # Pop and store the current string in variable `Y`
X # Push the list of replacement-pairs `X`
ε # Map over each pair:
Y # Push the current string `Y`
s # Swap so the current replacement-pair is at the top again
` # Pop and push both value in this pair separately
: # Do the replacement
} # Close the inner map
}« # After the outer map: merge it to the original list
˜ # Flatten it to a single list of strings
ê # Uniquify and (lexicographically) sort this list
é # Then sort an additional time by length
} # Close the changes-loop
Try the first two steps online.
Step 3: Since it's already being sorted correctly in the previous step, its first item is the result we want to output:
н # Pop and keep just the first/shortest item
# (which is output implicitly as result)
Charcoal, 101 bytes
≔⦃⦄ζFEη…κ⊕ι«≔⮌Φ⮌ιλε§≔ζΦιλε§≔ζε✂ι¹»⊞υθFυFζFE⌕Aι§ζκ⁺⁺…ιλκ✂ι⁺λLκLι¹«WΦλ№λײν≔⁻λײ⌊μλ¿¬№υλ⊞υλ»⌊Φυ⁼Lι⌊EυLλ
Attempt This Online! Link is to verbose version of code. Explanation:
≔⦃⦄ζFEη…κ⊕ι«≔⮌Φ⮌ιλε§≔ζΦιλε§≔ζε✂ι¹»
Expand the braid relations into a bidirectional mapping between substrings e.g. "bc": 3 becomes "bcb": "cbc", "cbc": "bcb".
⊞υθFυ
Start a breadth-first search of sequences of reflections.
Fζ
Loop over each mapping.
FE⌕Aι§ζκ⁺⁺…ιλκ✂ι⁺λLκLι¹«
Loop over all the possible reflections from this mapping.
WΦλ№λײν≔⁻λײ⌊μλ
Remove all reflection pairs.
¿¬№υλ⊞υλ
If this is a new result then add it to the search list.
»⌊Φυ⁼Lι⌊EυLλ
Output the minimum result of minimal length.