Consider the string 160615051. It can be "triangulated" as such:


Then, each row is a palindrome. Also note that each side on the perimeter is also a palindrome:

  1  |   1   |   
 6   |    6  |      
1    |     1 | 15051 

Therefore, this string can be considered to be a fully-palindromic triangle. Don't worry about the altitude 100 in this case, it doesn't have to be palindromic.

Input: A string of printable ASCII characters from 0x20 to 0x7E. This can be a character array, a single string, or an array of ASCII code points. Your input will always be able to be triangulated (that is, its length will always be a perfect square).

Output: A truthy value if the string is a fully-palindromic triangle, or a falsey value otherwise.

Test cases

input => output

1 => true
A => true
AAAA => true
nope => false
{{}} => false
1101 => true
1011 => false
1202 => false
111110001 => true
160615051 => true
160625052 => false
1111111111111111 => true
1121123211234321123454321 => true
HHeHHeleHHellleHHellolleH => true
HellolleHHellleHHeleHHeHH => false
111111111111111111111111111111111111 => true
abcbdefeddefgfedbcdefedcbabcdefedcba => true

15 Answers 15


Jelly, 14 12 bytes


Try it online!


We start by looking at the 0-based indices of the input string.

 H  H  e  H  H  e  l  e  H  H  e  l  l  l  e  H  H  e  l  l  o  l  l  e  H
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

To get the rows of the triangle, we can split the string before the indices 1, 1 + 3 = 4, 1 + 3 + 5 = 9, and 1 + 3 + 5 + 7 = 16. Since (n + 1)² = n² + (2n + 1), these sums are precisely the positive, perfect squares in the index list. If we also split the string before 0, this is as simple as splitting before all 0-based indices that are perfect squares.

After splitting, we get the following strings.


Next, we replace the empty string at the beginning with all the characters in the first column.


The task is now reduced to checking whether reversing all strings yields the same string array.

How it works

First J generates all 1-based indices of the input string J, then decrements them with to yield all 0-based indices. Ʋ tests all 0-based indices for squareness. For our example from above, this yields the following Boolean array.

 1  1  0  0  1  0  0  0  0  1  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0

Next, we call œṗ to partition the input string, e.g.,

 H  H  e  H  H  e  l  e  H  H  e  l  l  l  e  H  H  e  l  l  o  l  l  e  H

before all 1's (actually, all truthy elements). For our example, this yields the following string array.


Z⁻¦ is arguably the most interesting part of this answer. Let's analyze the more straightforward Z1¦ first.

¦ is the sparse quick. It consumes two links from the stack, specifically 1 and Z in this case. First Z is applied to its argument: the string array from before. Z is the zip atom and reads the string array / 2D character array by columns, yielding


What used to be left side of the input string and the first column of the string array now becomes the first string.

Now ¦ peeks at 1 and finds a single index: 1. Thus the first string in the original string array is replaced with the first string in the return value of Z; strings at other indices remain unaffected.


Let's call this array A.

We used Z⁻¦ instead of Z1¦, but this makes no difference: compares the string array with the input string for inequality, yielding 1 since they're not equal. The difference between the two is that Z⁻¦ is dyadic because is, allowing us to write œṗZ⁻¦ instead of œṗ¹Z1¦. This is because a dyad (œṗ) followed by a monad (œṗ¹Z1¦) is a fork (the monad is applied to the chain's argument / the input string, and the returned value is passed as the right argument to œṗ), while a dyad followed by another dyad (or at the end of the chain) is a hook, i.e., its right argument is the chain's argument.

All that's left to do is check for palindromicness. µ begins a new (monadic) chain who's argument is A. The upend atom U reverses all strings in A (but not A itself), then compares the result with A for equality. The returned Boolean 1 indicates a fully-palindromic triangle; other strings would return 0.

  • \$\begingroup\$ I really should learn how to read Jelly. (Explanation, please?) \$\endgroup\$ – CAD97 Jun 9 '17 at 5:31
  • 1
    \$\begingroup\$ I've edited my answer. \$\endgroup\$ – Dennis Jun 9 '17 at 5:39

Japt, 25 21 17 bytes

Saved 2 bytes thanks to @obarakon

ò@°T ¬v1

Test it online!

How it works

 ò@  ° T ¬ v1   // Implicit: U = input string, T = 0
UòXY{++T q v1}  // First line; reset U to the result of this line.
UòXY{        }  // Partition U at indices where
     ++T q      //   the square root of T incremented
           v1   //   is divisible by 1.
                // This breaks U at square indices, giving rows of 1, 3, 5, ... chars.
  Umg           // Take the first char of every item of U.
Up   )          // Append this to U.
      e         // Check that every item in the resulting array
       êP       // is a palindrome.
                // Implicit: output result of last expression

Note that we don't need to check both sides; if the sides are not the same, At least one of the rows is not a palindrome.

  • \$\begingroup\$ It this multiline thing a new feature of Japt? \$\endgroup\$ – Luke Jun 8 '17 at 21:02
  • \$\begingroup\$ @Luke Yes, I just added it Tuesday. This is my first chance to show it off :-) \$\endgroup\$ – ETHproductions Jun 8 '17 at 21:12
  • \$\begingroup\$ Never mind my golfing tip. It simply check whether every line was palindromic, which also happened to give correct results... \$\endgroup\$ – Luke Jun 8 '17 at 21:18

05AB1E, 18 bytes


Uses the 05AB1E encoding. Try it online!

  • \$\begingroup\$ You can save one byte by modifying it slightly gÅÉ£ÐíQs€¬āÈÏJÂQ* \$\endgroup\$ – kalsowerus Jun 9 '17 at 6:05
  • \$\begingroup\$ gÅÉ£ you sly fox... I underestimate those list-commands.py \$\endgroup\$ – Magic Octopus Urn Jun 9 '17 at 16:25

Jelly, 18 16 bytes


Try it online!

Thanks to Jonathan Allan for trivial but not so obvious -2 byte savings.

  • \$\begingroup\$ Use my triangle construction and save a byte: JƲ0;œṗ⁸ZḢ$ṭ$ŒḂ€Ạ \$\endgroup\$ – Jonathan Allan Jun 8 '17 at 23:39
  • \$\begingroup\$ ...in fact combine that idea with the untruth and save another byte, since partitioning will "zip shortest": J²‘Ṭœṗ⁸ZḢ$ṭ$ŒḂ€Ạ \$\endgroup\$ – Jonathan Allan Jun 9 '17 at 0:31
  • \$\begingroup\$ @JonathanAllan Umm...why do I need to ½ at all? Now J makes more sense... \$\endgroup\$ – Erik the Outgolfer Jun 9 '17 at 7:48

JavaScript (ES6), 112 bytes


t and u collect the sides so that they can be tested at the end.


C#, 184 bytes

using System.Linq;
f=>{string c=f[0]+"",d=c,e="";for(int i=1,k=1,s=f.Length;i<s;)
{c+=f[i];d+=f[(i+=k+=2)-1];e=f.Substring(s-k);}return b(c)&b(d)&b(e);}

Thought the solution was looking good until I got to the palindrome part

Ungolfed version:

Func<string, bool> b = a => string.Concat(a.Reverse()) == a;
        Func<string, bool> func = f => {

            string c = f[0] + "", d = c, e = "";

            for (int i = 1, k = 1, s = f.Length; i < s;) {
                c += f[i];
                d += f[(i += k += 2) - 1];
                e = f.Substring(s - k);

            return b(c) & b(d) & b(e);
  • \$\begingroup\$ Can you move e=.. into the for loop line to save a byte? There's no need to count the newlines into the byte count so I'm assuming you're not. \$\endgroup\$ – TheLethalCoder Jun 9 '17 at 13:37
  • \$\begingroup\$ No i'm not counting newlines, I can't move the e into the loop cause I need it in the return statement. \$\endgroup\$ – LiefdeWen Jun 9 '17 at 15:47
  • \$\begingroup\$ I meant like this ....; i < s;e = f.Substring(s - k)){c+=.... \$\endgroup\$ – TheLethalCoder Jun 9 '17 at 15:48

Java 8, 358 301 bytes

import java.util.*;s->{List<String>l=new Stack();for(int i=0,p=1,t=1;p<=s.length();p+=t+=2)l.add(s.substring(i,i=p));String a="",b=a;for(String q:l){a+=q.charAt(0);b+=q.charAt(q.length()-1);}return p(a)&p(b)&p(l.get(l.size()-1));}boolean p(String s){return s.equals(new StringBuffer(s).reverse()+"");}

Input is a String, output is a boolean.


Try it here.

import java.util.*;               // Required import for List and Stack

s->{                              // Method (1) with String parameter and boolean return-type
  List<String>l=new Stack();      //  Create a String-list
  for(int i=0,p=1,t=1;            //  Initialize some index/counter integers
      p<=s.length();              //  Loop (1) over the String in sections
      p+=t+=2)                    //    And increase `p` like this after every iteration: 1,4,9,16,25,etc.
    l.add(s.substring(i,i=p));    //   And add a substring-section to the list (0,1 -> 1,4 -> 4,9 -> 9,16 -> etc.)
                                  //  End of loop (1) (implicit / single-line body)
  String a="",b=a;                //  Two temp Strings
  for(String q:l){                //  Loop (2) over the list
    a+=q.charAt(0);               //   And append the first character to String `a`
    b+=q.charAt(q.length()-1);    //   And the last character to String `b`
  }                               //  End of loop (2)
  return p(a)                     //  Return if String `a` is a palindrome
        &p(b)                     //   as well as String `b`
        &p(l.get(l.size()-1));    //   as well as the last String in the list
}                                 // End of method (1)

boolean p(String s){              // Method (2) with String parameter and boolean return-type
  return s.equals(new StringBuffer(s).reverse()+"");
                                  //  Return if this String is a palindrome
}                                 // End of method (2)

Jelly,  20  21 bytes

+2 bytes - I released buggy code :(
-1 byte - moved from moulding like odd integers to partitioning at square indexes


A monadic link accepting a list of characters and returning 1 (Truthy) or 0 (Falsey).
Note: this uses the part of the specification that limits the input to a square length.

Try it online! or see the test suite.

This may be simplified to 17 bytes by noting that if all rows are palindromes only one "side" needs checking (JƲ0;œṗ⁸ZḢ$ṭ$ŒḂ€Ạ), however Erik the Outgolfer already noticed this fact and used it in their answer so I have given the triangle construction method to them saving a byte there.

Additionally, that may in turn be improved to 16 bytes by noting that partitioning at truthy indexes does not mind if there is excess in the left argument (J²‘Ṭœṗ⁸ZḢ$ṭ$ŒḂ€Ạ).


JƲ0;œṗµ2BịЀ⁸Z;⁸ŒḂ€Ạ - Link: list, a      e.g. "abcbazxza"
J                     - range of length of a  = [1,2,3,4,5,6,7,8,9]
 Ʋ                   - is square? (vectorises) [1,0,0,1,0,0,0,0,1]
   0;                 - prepend a zero        [0,1,0,0,1,0,0,0,0,1]
     œṗ               - partition a at 1s     ["a","bcb","azxza"]
       µ              - monadic chain separation, call that t
        2B            - 2 in binary = [1,0]
             ⁸        - chain's left argument, t
          ịЀ         - map with index into    ["aa","bb","aa"] (1st and last of each of t)
              Z       - transpose              ["aba","aba"] (left and right "sides" of t)
               ;⁸     - concatenate t          ["aba","aba","a","bcb","azxza"]
                 ŒḂ€  - palindromic? for €ach  [1,1,1,1,1]
                    Ạ - all?                   1
  • 1
    \$\begingroup\$ Darn, I was just about to make a Jelly answer. Though technically mine is wrong and like twice as long... nice job :) :P \$\endgroup\$ – HyperNeutrino Jun 8 '17 at 20:36
  • \$\begingroup\$ "noting that partitioning at truthy indexes does not mind if there is excess in the left argument" noticed too before reading. \$\endgroup\$ – Erik the Outgolfer Jun 9 '17 at 7:51

Mathematica, 156 bytes




  • \$\begingroup\$ Can't you replace If[<stuff>, True, False] with just <stuff>? And I think And@@(...) is shorter than Count[...,True]==s, which also means you don't have to define s as a variable. \$\endgroup\$ – Not a tree Jun 9 '17 at 6:02
  • \$\begingroup\$ Wait, does this actually test the diagonals? I'm getting false positives for a couple of the test cases ("1202" and "160625052"). \$\endgroup\$ – Not a tree Jun 9 '17 at 6:05
  • \$\begingroup\$ all problems fixed \$\endgroup\$ – J42161217 Jun 9 '17 at 7:51

PHP, 97 bytes


Try it online!

  • \$\begingroup\$ Wait, does this actually test the diagonals? I'm getting false positives for a couple of the test cases ("1202" and "160625052"). \$\endgroup\$ – J42161217 Jun 9 '17 at 7:54
  • \$\begingroup\$ @Jenny_mathy now it works \$\endgroup\$ – Jörg Hülsermann Jun 9 '17 at 8:07

Java, 136 bytes

l->{for(int i=0,j,k=1;i<l.size();i=j,k+=2)if(!l.subList(i,j=i+k).equals(l.subList(i,j).asReversed().toList()))return false;return true;}

Uses a MutableList<Character> from Eclipse collections

Function<MutableList<Character>, Boolean> func = l->{
   for(int i=0,j,k=1;i<l.size();i=j,k+=2)  // `i` is the start index, `j` is the end index, `k` increments by 2
       if(!l.subList(i,j=i+k).equals( //Check that the first partition equals
           l.subList(i,j).asReversed().toList())  // The same sublist reversed
       return false;
   return true;

Perl 5, 81 + 1 (-p) = 82 bytes

$a[0].=$1while($a[++$q]=substr$_,0,($#i+=2),'')=~/(.)/;$\||=$_ ne reverse for@a}{

Try it online!

Outputs undef (i.e., blank, null) for true, any number for false


Excel VBA, 87 Bytes

Anonymous VBE immediate window function that takes input from cell [A1] and outputs to the VBE immediate window

k=1:For i=1To[Len(A1)^.5]:s=Mid([A1],j+1,i*2-1):j=j+i*2-1:k=k*(s=StrReverse(s)):Next:?k

Python 2, 128 118 115 bytes

def f(s):
 while s:l+=s[0];r+=s[w-1];b+=[s[:w]];s=s[w:];w+=2
 print all(d==d[::-1]for d in[l,r]+b)

Try it online!


APL (Dyalog Extended), 38 bytes


Try it online!

The cuts are of size 1,3,5,7..., so that is used with to get the appropriate array.

Requires ⎕IO←0 for zero indexing.

Shortened with a lot of help from Marshall and dzaima.


                                   ≢⍵  length of the string
                                 w←    assign to var w
                                ⍳      range from 0..length-1
                            1+2×       convert each to 2n+1 (odd numbers)
                         ⍴⍨¨           replicate each element times itself
                        ∊              flatten that
                      w⍴               reshape to string's length
                   ⍵⊆⍨                 partition the string based on that
       (⊢,⊢/¨,⍥⊂⊃¨)                    Train:
       (⊢,        )                    concatenate the partition with:
       (  ⊢/¨     )                    last character of each partition
       (        ⊃¨)                    first character of each partition
       (      ⍥⊂  )                    both enclosed into two strings
  ⌽¨                                   do the reverses of each part
    ≡                                  match
     ⊢                                 the parts?                           

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.