g | x | w | all
Bytes Lang Time Link
042Haskell250212T214010Zxnor
053JavaScript Node.js250209T234015Zemanresu
017Charcoal250209T215657ZNeil
008Jelly250210T205300ZUnrelate
047R250210T120707Zpajonk
082Maple250209T235801Zdharr
037APLNARS250209T190525ZRosario
014Japt250210T102734ZShaggy
01005AB1E250210T084252ZKevin Cr
082Google Sheets250209T175106Zdoubleun
010Jelly250209T162908ZJonathan
056Python 3250209T090856Zxnor
023Retina 0.8.2250209T080720ZNeil
059JavaScript Node.js250209T023102Zl4m2

Haskell, 42 bytes

(.g).(==).g
g s=elem 'P's<$s++['T'|'T'<-s]

Try it online!

The top line is point-free for \a b->g a==g b. The "fingerprint" function g appends to the input s an additional character for each T in s, yielding a length of one per P and two per T. The entries of this list/string are all replaced via <$ with a Boolean of whether the list has any P's, to tell apart all-T lists.

The [?|'T'<-s] can use any character for the ?, since they'll be replaced by the Boolean anyway, but Haskell's type-checking prevents something shorter like a number or the variable s.

JavaScript (Node.js), 55 53 bytes

x=>y=>g(x)==g(y)
g=x=>x.split`T`.join`PP`+/P/.test(x)

Try it online!

Uses xnor's P fingerprint idea, and I borrowed l4m2's test harness. -2 bytes thanks to Arnauld.

As per xnor's answer, we replace T with PP, but need some way to check whether there was a catalyst P in the first place - this is done via appending /P/.test(x), which is coerced to either "true" or "false".

Charcoal, 21 17 bytes

⁼⭆⁺⁻θPθ⌊θ⭆⁺⁻ηPη⌊η

Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - for equal, nothing if not. Explanation: Now a port of @UnrelatedString's approach; strings containing only Ts are doubled in length but strings that (also) contain Ps have any Ts doubled and changed into Ps.

    θ               First input
   ⁻                Remove occurrences of
     P              Literal string `P`
  ⁺                 Concatenated with
      θ             First input
 ⭆                  Replace all characters with
        θ           First input
       ⌊            Minimum (`P` if there is one, else `T`)
⁼                   Equals
         ⭆⁺⁻ηPη⌊η   As above but using the second input
                    Implicitly print

Note that the Minimum is evaluated inside the loop, thus it will never be called for empty strings.

Jelly, 8 bytes

f”T;aṂ)E

Try it online!

Wanted to beat this with something using Ġ, but the best I could get was the fairly silly ĠẈUḄ×Ṃ)E which ties.

      )E    Are the elements of the input equal under?:
f”T         Filter to only "T"s
   ;        and concatenate to unfiltered.
    aṂ      Replace all elements with the minimum. ("P" if it exists)

R, 49 47 bytes

\(x)all(table(gsub("T","PP",x),grepl("P",x))>1)

Attempt This Online!

Port of @xnor's fingerprint solution.

Maple, 82 bytes

(s,t)->evalb(`=`((p:=(c:=x->numboccur~([s,t],x)[])(P))+`if`(`*`(p)=0,-1,2)*c(T)));

Input is two lists containing Ps and Ts. The TP->PPP rule means that, provided we have at least one P, we just need to check if the number of Ps plus twice the number of Ts is the same for each list.

Function c(X) counts the number of Xs (X=P or X=T) in each list and outputs a sequence like (2,0). If c(P) has a zero (detected by the product being zero), we need to check that c(P) - c(T) has the same components, otherwise we need to check c(P) + 2*c(T) has the same two components. Actually, we define a multiplier m=-1 if c(P) has a zero and m=2 otherwise, and then check if the two components of c(P)+m*c(T) are the same.

APL(NARS), 37 chars

{≡/{'P'∊k←⍵:∊k⊣k[⍸⍵='T']←⊂'PP'⋄k}¨⍺⍵}

the nested function, if P is element of the string, replace each 'T' char with 2 chars 'PP' and return the string, else return the string of input unchanged.

It would return 1 for true 0 for false. Test:

     f←{≡/{'P'∊k←⍵:∊k⊣k[⍸⍵='T']←⊂'PP'⋄k}¨⍺⍵}

  ''f'' 
1
  ''f'P' 
0
  ''f'T' 
0
  'TT'f'TT' 
1
  'PTPTPPP'f'PPPPPTPP' 
1

Japt, 14 bytes

Ëñ e"PT"'P³Ãr¶

Try it (includes all test cases)

05AB1E, 10 bytes

€{„PT¬3×:Ë

Input as a pair of strings.

Try it online or verify all test cases.

Explanation:

€          # Map over both strings in the (implicit) input-pair:
 {         #  Sort its characters
  „PT      # Push string "PT"
     ¬     # Push its first character (without popping): "P"
      3×   # Repeat it three times: "PPP"
        :  # Replace all "PT" for "PPP" until no "PT" are left
         Ë # Check if both strings in the pair are the same
           # (which is output implicitly as result)

Google Sheets, 82 bytes

=let(f,lambda(s,substitute(regexreplace(s,"^(T*)T$","$1_"),"T","PP")),f(A1)=f(B1))

Uses xnor's method.

screenshot

Jelly, 10 bytes

9 if we may accept lists of code points (remove O); 7 if we may accept lists of "is T"s (remove Oọ3).

Oọ3~Ạ¡€‘§E

A monadic Link that accepts a pair of lists of PT characters and yields 1 if equivalent or 0 if not.

Try it online!

How

Oọ3~Ạ¡€‘§E - Link: list of lists of characters, [A, B]
O          - ordinals (P -> 80; T -> 84)
 ọ3        - multiplicity of three (P -> 0; T -> 1)
      €    - for each {list, L, of 0s and 1s}:
     ¡     -   repeat...
    Ạ      -   ...times: all? (i.e. Once iff only Ts existed)
   ~       -   ...action: bitwise NOT (replaces all the values (1s) with -2s)
       ‘   - increment all these 0s, 1s and -2s (P -> 1; T -> 2 or -1 if all were T)
        §  - sums
         E - equal?

Python 3, 56 bytes

lambda*l:len({s.replace('T','PP',len(s)-1)for s in l})<2

Try it online!

Replaces every T in each string with PP before checking for equality. But, to tell apart strings of only T with no "catalyst" P, the number of replacements is capped at len(s)-1, which leaves one T unconverted in such strings.

57 bytes

lambda*l:len({(s.replace('T','PP'),'P'in s)for s in l})<2

Try it online!

Here the fingerprinting function is (s.replace('T','PP'),'P'in s), which can be more easily with a helper function g (62 bytes)

lambda a,b:g(a)==g(b)
g=lambda s:(s.replace('T','PP'),'P'in s)

We effectively get the number of P's plus double the number of T's, but to separate out the all-T's case, we also record a bit for whether the string has any P's.

55 bytes

lambda*l:len({s.replace('T',min(s+'~')*2)for s in l})<2

Try it online!

If not for empty inputs, we could get rid if +'~'

Retina 0.8.2, 23 bytes

%O`.
+`PT
PPP
^(.*)¶\1$

Try it online! Takes input on separate lines but link is to test suite that splits on commas for convenience. Explanation:

%O`.

Sort each input separately.

+`PT
PPP

Convert Dyck's surface to a chosen "canonical" representation.

^(.*)¶\1$

Compare the two results.

JavaScript (Node.js), 59 bytes

x=>y=>g(x)==g(y)
g=x=>x.replace(/T(?=.*P)|T(?<=P.*)/g,'PP')

Try it online!

if backtrack (?<=P.*) unavailable then

JavaScript (Node.js), 60 bytes

x=>y=>g(x)==g(y)
g=x=>x==(x=x.replace(/TP|PT/,'PPP'))?x:g(x)

Try it online!