Fully-palindromic triangles

18

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

  1
 606
15051

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

Conor O'Brien

Posted 2017-06-08T20:15:39.483

Reputation: 36 228

Answers

10

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.

Dennis

Posted 2017-06-08T20:15:39.483

Reputation: 196 637

I really should learn how to read Jelly. (Explanation, please?) – CAD97 – 2017-06-09T05:31:07.877

1I've edited my answer. – Dennis – 2017-06-09T05:39:12.767

6

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.

ETHproductions

Posted 2017-06-08T20:15:39.483

Reputation: 47 880

It this multiline thing a new feature of Japt? – Luke – 2017-06-08T21:02:01.363

@Luke Yes, I just added it Tuesday. This is my first chance to show it off :-) – ETHproductions – 2017-06-08T21:12:01.497

Never mind my golfing tip. It simply check whether every line was palindromic, which also happened to give correct results... – Luke – 2017-06-08T21:18:33.927

4

05AB1E, 18 bytes

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

Uses the 05AB1E encoding. Try it online!

Adnan

Posted 2017-06-08T20:15:39.483

Reputation: 41 965

You can save one byte by modifying it slightly gÅÉ£ÐíQs€¬āÈÏJÂQ* – kalsowerus – 2017-06-09T06:05:32.220

gÅÉ£ you sly fox... I underestimate those list-commands.py – Magic Octopus Urn – 2017-06-09T16:25:10.073

4

Jelly, 18 16 bytes

J²‘Ṭœṗ⁸ZḢ$ṭ$ŒḂ€Ạ

Try it online!

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

Erik the Outgolfer

Posted 2017-06-08T20:15:39.483

Reputation: 38 134

Use my triangle construction and save a byte: JƲ0;œṗ⁸ZḢ$ṭ$ŒḂ€Ạ – Jonathan Allan – 2017-06-08T23:39:18.760

...in fact combine that idea with the untruth and save another byte, since partitioning will "zip shortest": J²‘Ṭœṗ⁸ZḢ$ṭ$ŒḂ€Ạ – Jonathan Allan – 2017-06-09T00:31:00.117

@JonathanAllan Umm...why do I need to ½ at all? Now J makes more sense... – Erik the Outgolfer – 2017-06-09T07:48:16.110

3

JavaScript (ES6), 112 bytes

f=(s,n=1,t='',u='',g=([...a])=>''+a==a.reverse())=>s?g(s.slice(0,n))&f(s.slice(n),n+2,t+s[0],u+s[n-1]):g(t)&g(u)

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

Neil

Posted 2017-06-08T20:15:39.483

Reputation: 95 035

2

C#, 184 bytes

using System.Linq;
b=a=>string.Concat(a.Reverse())==a
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);
        };

LiefdeWen

Posted 2017-06-08T20:15:39.483

Reputation: 3 381

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. – TheLethalCoder – 2017-06-09T13:37:39.197

No i'm not counting newlines, I can't move the e into the loop cause I need it in the return statement. – LiefdeWen – 2017-06-09T15:47:21.567

I meant like this ....; i < s;e = f.Substring(s - k)){c+=.... – TheLethalCoder – 2017-06-09T15:48:22.803

2

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.

Explanation:

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)

Kevin Cruijssen

Posted 2017-06-08T20:15:39.483

Reputation: 67 575

1

Jelly,  20  21 bytes

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

JƲ0;œṗ⁸ZḢ$ṭ$ŒḂ€Ạ

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Ḣ$ṭ$ŒḂ€Ạ).

How?

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

Jonathan Allan

Posted 2017-06-08T20:15:39.483

Reputation: 67 804

1Darn, I was just about to make a Jelly answer. Though technically mine is wrong and like twice as long... nice job :) :P – HyperNeutrino – 2017-06-08T20:36:52.740

"noting that partitioning at truthy indexes does not mind if there is excess in the left argument" noticed too before reading. – Erik the Outgolfer – 2017-06-09T07:51:44.033

1

PHP, 97 bytes

for($t=1;~$s=substr($argn,$i**2,$i++*2+1);$d.=$s[0])$t&=strrev($s)==$s;$t&=strrev($d)==$d;echo$t;

Try it online!

Jörg Hülsermann

Posted 2017-06-08T20:15:39.483

Reputation: 13 026

Wait, does this actually test the diagonals? I'm getting false positives for a couple of the test cases ("1202" and "160625052"). – J42161217 – 2017-06-09T07:54:49.843

@Jenny_mathy now it works – Jörg Hülsermann – 2017-06-09T08:07:30.733

1

Mathematica, 156 bytes

B=StringTake;Count[PalindromeQ/@Join[A=Table[B[#,{i^2+1,(i+1)^2}],{i,0,(s=Sqrt@StringLength@#)-1}],{StringJoin@Table[B[A[[i]],1],{i,Length@A}]}],True]==s+1&


input

["1101"]

J42161217

Posted 2017-06-08T20:15:39.483

Reputation: 15 931

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. – Not a tree – 2017-06-09T06:02:07.287

Wait, does this actually test the diagonals? I'm getting false positives for a couple of the test cases ("1202" and "160625052"). – Not a tree – 2017-06-09T06:05:48.213

all problems fixed – J42161217 – 2017-06-09T07:51:27.240

1

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;
};

Nathan Merrill

Posted 2017-06-08T20:15:39.483

Reputation: 13 591

1

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

Xcali

Posted 2017-06-08T20:15:39.483

Reputation: 7 671

0

Python 2, 128 118 115 bytes

def f(s):
 w=1;l=r='';b=[]
 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!

TFeld

Posted 2017-06-08T20:15:39.483

Reputation: 19 246

0

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

Taylor Scott

Posted 2017-06-08T20:15:39.483

Reputation: 6 709