Fully-palindromic triangles

Jelly, 14 12 bytes

J’ƲœṗZ⁻¦µU⁼

Try it online!

Background

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.

""
"H"
"HeH"
"HeleH"
"HellleH"
"HellolleH"

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

"HHHHH"
"H"
"HeH"
"HeleH"
"HellleH"
"HellolleH"

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.

['', 
 'H',
 'HeH',
 'HeleH',
 'HellleH',
 'HellolleH'
]

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

['HHHHH',
 'eeee',
 'Hlll',
 'ell',
 'Hlo',
 'el',
 'Hl',
 'e',
 'H'
]

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.

['HHHHH',
 'H',
 'HeH',
 'HeleH',
 'HellleH',
 'HellolleH'
]

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.


Japt, 25 21 17 bytes

Saved 2 bytes thanks to @obarakon

ò@°T ¬v1
pUmg)eêP

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.
 pUmg)eêP
UpUmg)eêP
  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.


05AB1E, 18 bytes

gÅÉ£õÜÐíQs€¬āÈÏÂQ*

Uses the 05AB1E encoding. Try it online!