22
\$\begingroup\$

Background

Inspired by I'm a palindrome. Are you?, where it is presented the shocking fact that “()() is not a palindrome, but ())(”, I asked myself what instead is ()() and the answer is simply: it is a string with a vertical symmetry axis!

The task

Write a program or function that takes a string S (or the appropriate equivalent in your language) as input, checks for symmetry along the vertical axis, and returns a truthy or falsy value accordingly. You can use any reasonable means to take the input and provide the output.

Reflectional symmetry

Reflectional symmetry around a vertical axis (or left-right symmetry) means that if you put a mirror vertically at the exact center of the string, the reflected image of the first half of the string is identical to the second half of the string.

For example, the following strings are reflectional symmetric around a vertical axis:

()()
()()()
[A + A]
WOW ! WOW
OH-AH_wx'xw_HA-HO
(<<[[[T*T]]]>>)
(:)
)-(
())(()
qpqp

while the following are not:

())(
((B))
11
+-*+-
WOW ! wow
(;)
qppq

Rules of the contest

• Your program or function will receive only printable ASCII characters. You can include or not the empty string, (which is symmetric, of course!) as legal input, which is better for you.

• The ASCII characters that can be considered symmetric with respect to the vertical axes are the following (note the initial space, and the difference between uppercase and lowercase letters):

 !"'+*-.:=AHIMOTUVWXY^_ovwx|

The ASCII characters that can be considered “mirrored” and their corresponding characters are:

()<>[]{}qpbd/\

Note that, since they are mirrored, you can have both () as well as )(, /\ and \/, etc.

All the other ASCII printable characters must be considered asymmetric and without a mirrored corresponding character.

• This is a challenge: the shorter your program is, measured in bytes, the better, in any programming language.

• Kudos to people that will produce a symmetric program!

Note: this question is not a duplicate of "Convenient Palindrome”, that requires to check for palindromic strings in which parentheses are flipped. This question is different for two reasons:

1) it is a restriction of the other question for what concerns non-parentheses characters, since only symmetric characters can appear in reverse order.

2) Since it is based on the concept of symmetry, and not on a concept of “convenient palindrome”, mirrored characters can appear in both order, i.e. [] and ][, and this makes the program to solve it different from programs that solve the other problem.

\$\endgroup\$
  • 6
    \$\begingroup\$ Just for anybody wondering, Charcoal doesn't reflect letters. :( \$\endgroup\$ – totallyhuman Sep 9 '17 at 15:15
  • 4
    \$\begingroup\$ I disagree with the dupeyness, as the dupe target does not reflect letters and this does. \$\endgroup\$ – Stephen Sep 9 '17 at 17:02
  • \$\begingroup\$ Sorry, I missed the examples, my mistake \$\endgroup\$ – jrtapsell Sep 9 '17 at 18:29
  • 6
    \$\begingroup\$ Why isn't 8 considered "symmetric"? \$\endgroup\$ – Scott Milner Sep 9 '17 at 19:36
  • 2
    \$\begingroup\$ @FunkyComputerMan It's not essentially the same as the dupe target at all. First and foremost this has no source code restriction. \$\endgroup\$ – Jonathan Allan Sep 11 '17 at 18:17
7
\$\begingroup\$

JavaScript (ES6), 130 125 113 bytes

f=
s=>s==[...s].reverse(s=`()<>[]{}qpbd/\\`).map(c=>s[s.indexOf(c)^1]||/[- !"'+*.:=AHIMOT-Y^_ovwx|]/.exec(c)).join``
<input oninput=o.textContent=f(this.value)><pre id=o>

Edit: Saved 5 bytes thanks to @Arnauld. Saved a further 11 bytes thanks to @YairRand.

\$\endgroup\$
  • \$\begingroup\$ Could you use a regexp instead of includes()? Such as /[- !"'+*.:=AHIMO^_ovwx|T-Y]/.test(c). \$\endgroup\$ – Arnauld Sep 9 '17 at 20:00
  • \$\begingroup\$ @Arnauld Indeed, that range is very helpful, thanks! \$\endgroup\$ – Neil Sep 9 '17 at 20:16
  • \$\begingroup\$ You can golf [...s].reverse().map(...) to: s::[].map().reverse() if you are ok with using new ES-next features. link \$\endgroup\$ – Downgoat Sep 12 '17 at 3:03
  • \$\begingroup\$ @Downgoat Do you have a link to the spec of that feature? \$\endgroup\$ – Neil Sep 12 '17 at 7:47
  • 1
    \$\begingroup\$ @Neil here \$\endgroup\$ – Downgoat Sep 12 '17 at 21:46
5
\$\begingroup\$

Jelly, 69 62 bytes

“(<[{qb/“ !"'+*-.:=AHIMOTUVWXY^_ovwx|“)>]}pd\”,Ṛ$F©f@ð®œs2¤yU⁼

Try it online!

All test cases

-7 bytes thanks to @JonathanAllan

How it Works

“(<[{qb/“ !"'+*-.:=AHIMOTUVWXY^_ovwx|“)>]}pd\”,Ṛ$F©f@ð®œs2¤yU⁼  main link

“(<[{qb/“ !"'+*-.:=AHIMOTUVWXY^_ovwx|“)>]}pd\”  The literal list of strings  ['(<[{qb/', ' !"\'+*-.:=AHIMOTUVWXY^_ovwx|', ')>]}pd\\']
               $                 Last two links (if not part of an LCC) as a monad 
            Ṛ                    Reverse array Does not vectorize.
           ,                     Pair; return [x, y].
                 ©               Copy link result to register (® atom to retrieve). 
              F                  Flatten list.
                  f              Filter; remove the elements from x that are not in y.
                   @             Swaps operands. 

                    ð            Start a new dyadic chain
                         ¤       Nilad followed by links as a nilad. 
                      2          The literal integer 2
                   ®             Restore; retrieve the value of the register. Initially 0.
                    œs           Split x into y chunks of similar lengths.
                          y      Translate the elements of y according to the mapping in x.
                           U     Upend; reverse an array.
                            ⁼    Equals. Does not vectorize.
\$\endgroup\$
  • \$\begingroup\$ Save six bytes using a filter: ¢FiЀ;1Ạðaµ¢yU⁼ -> ¢Ff@ð¢yU⁼ \$\endgroup\$ – Jonathan Allan Sep 9 '17 at 16:30
  • \$\begingroup\$ Save another by using the register (all on one line now): ...}pd\”,Ṛ$Fœs©2Ff@ð®yU⁼ \$\endgroup\$ – Jonathan Allan Sep 9 '17 at 16:37
  • \$\begingroup\$ (...although at the same length ...}pd\”,Ṛ$F©f@ð®œs2¤yU⁼ is arguably nicer) \$\endgroup\$ – Jonathan Allan Sep 9 '17 at 16:45
  • \$\begingroup\$ Found another one byte save by only encoding for one of each pair of adjacent ordinals from the symmetric set (edit of deleted comment with better code) \$\endgroup\$ – Jonathan Allan Sep 9 '17 at 20:19
4
\$\begingroup\$

Python 3, 211 208 195 bytes

lambda S,p="()<>[]{}qpbd\/",s=" !\"'+*-.:=AHIMOTUVWXY^_ovwx|":(S==S.translate({ord(s[2*x]):s[2*x+1]for s in(p,p[::-1])for x in range(7)})[::-1])*(~len(S)%2*s[len(S)//2]in s)*(not set(S)-set(p+s))

Saved 13 bytes thanks to Jonathan Allan.

\$\endgroup\$
  • 1
    \$\begingroup\$ Save 9 bytes: 1. reverse order of slashes so no need to escape; 2. use 2*x and range(7); 3. use multiplication to avoid the >2 test; 4. use bitwise not on the len(S) to avoid the not of not len(S)%2; 5. use the fact that ''in'blah' is True to allow the string multiplication ~len(S)%2*s[len(S)//2]in s. \$\endgroup\$ – Jonathan Allan Sep 9 '17 at 21:14
  • 1
    \$\begingroup\$ Save 4 more inlining everything \$\endgroup\$ – Jonathan Allan Sep 9 '17 at 21:36
2
\$\begingroup\$

SOGL V0.12, 88 bytes

"el²┘N!←8mYdDm⁵╔C⅛┌6▼ģη⁷fņ‘;W‽0←}C
l»{Kα}lalh=‽;KCø;{:↔³↔=?"qpbd”⁴²+:GW:2%«H+W}:h=?:CΚ}=

Try it Here!

~24 bytes to add qpbd mirroring and 6 bytes for (x-1 XOR 1) + 1 :/

\$\endgroup\$
2
\$\begingroup\$

Kotlin 1.1, 201 199 bytes

{var R="(<[{qb/\\dp}]>)"
var m=HashMap<Any,Any>()
"\"!'+*-.:=AHIMOTUVWXY^_ovwx| ".map{m[it]=it}
R.indices.map{m[R[it]]=R[R.length-(it+1)]}
it.zip(it.reversed()).filter{m[it.first]!=it.second}.none()}

Beautified

{
    var R = "(<[{qb/\\dp}]>)"
    var m = HashMap<Any, Any>()
    "\"!'+*-.:=AHIMOTUVWXY^_ovwx| ".map { m[it] = it }
    R.indices.map { m[R[it]] = R[R.length - (it + 1)] }
    it.zip(it.reversed()).filter { m[it.first] != it.second }.none()
}

Test

var i:(String)->Boolean =
{var R="(<[{qb/\\dp}]>)"
var m=HashMap<Any,Any>()
"\"!'+*-.:=AHIMOTUVWXY^_ovwx| ".map{m[it]=it}
R.indices.map{m[R[it]]=R[R.length-(it+1)]}
it.zip(it.reversed()).filter{m[it.first]!=it.second}.none()}
fun main(args: Array<String>) {
    var GOOD = listOf("()()",
            "()()()",
            "[A + A]",
            "WOW ! WOW",
            "OH-AH_wx'xw_HA-HO",
            "(<<[[[T*T]]]>>)",
            "(:)",
            ")-(",
            "())(()",
            "qpqp")

    var BAD = listOf("())(",
            "((B))",
            "11",
            "+-*+-",
            "WOW ! wow",
            "(;)",
            "qppq")

    GOOD.filterNot { i(it) }.forEach { throw AssertionError(it) }
    BAD.filter { i(it) }.forEach { throw AssertionError(it) }
    println("Test Passed")
}

Can't run on TIO because 1.1 is not supported

\$\endgroup\$
  • \$\begingroup\$ You can get it to work on 1.0 by just importing HashMap Try it online! \$\endgroup\$ – CAD97 Sep 10 '17 at 2:02
  • \$\begingroup\$ Would my score be with or without the import? \$\endgroup\$ – jrtapsell Sep 10 '17 at 9:38
  • \$\begingroup\$ since the import is only a shim to get it to work on 1.0, where it works on 1.1 as is, so long as the answer specifies 1.1 you'd be scored without the import. I'd put a note in though, just in case someone doesn't know HashMap is (effectively) imported in 1.1 automatically. \$\endgroup\$ – CAD97 Sep 10 '17 at 17:16
2
\$\begingroup\$

Python 2, 182 167 163 162 160 158 bytes

lambda s:s[::-1]==s.translate(m(t+w,w+t),m("","").translate(None," !\"'+*-.:=AHIMOTUVWXY^_ovwx|"+t+w))
from string import*
m=maketrans
t=")>[{/qd"
w="(<]}\pb"

Try it online!

Saved 2 bytes thanks to Jonathan Allan

Explanation First, we need to build the list of all chars that don't have a symmetric (the char itself : A, ... or another char ( for ), ...):

  • m("","") returns a string with all the available chars.

  • m("","").translate(None," \t!\"'+*-.:=AHIMOTUVWXY^_ovwx|"+t+w)) removes from all the available chars the chars that have a symmetric.

Then, we map every char to its symmetric char and remove the chars that don't have a symmetric with s.translate(m(t+w,w+t),<chars that don't have a symmetric>)

If the result equals to the reversed string, we have a symmetric string.

\$\endgroup\$
  • \$\begingroup\$ If you move the slashes from the right end of t and w you can forego the escape, e.g. w="(<]{\pb". Save another byte with from string import*;m=maketrans (I personally go for a newline when the ; does not save bytes though). Also you do not need to name the function so long as it is reusable and not recursive, which saves another 2. \$\endgroup\$ – Jonathan Allan Sep 10 '17 at 11:39
  • \$\begingroup\$ Also you do not need to name the function so long as it is reusable and not recursive, which saves another 2 TIO (note: the code you have listed and at your link is 162 bytes) \$\endgroup\$ – Jonathan Allan Sep 10 '17 at 11:48
  • \$\begingroup\$ @JonathanAllan thanks. I had already removed (mentally) the two bytes for f=, but your version is cleaner. \$\endgroup\$ – jferard Sep 11 '17 at 7:35
1
\$\begingroup\$

Perl 5, 102 + 1 (-p) = 103 bytes

$_=!/[^ !"'+*.:=AHIMOT-Y^_ovwx|()<>[\]{}qpbd\/\\-]/&&$_ eq reverse y|()<>[]{}qpbd/\\|)(><][}{pqdb\\/|r

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Scala, 140 bytes

s.zip(s.reverse).forall(c=>(" !\"'+*-.:=AHIMOTUVWXY^_ovwx|".flatMap(x=>x+""+x)+"()<>[]{}qpbd/\\\\/dbpq}{][><)(").indexOf(c._1+""+c._2)%2==0)

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.