# Python: can I shorten this code further for checking substrings

So the golf question was like this:

You are given a number T and then a total of T number of 17-character strings. For each of the strings, output yes if it contains four consecutive identical characters or contains the substring DX, else output no.

The following constraints are promised for each of the 17-character strings:

1. The possible character set is DX2AKQJT9876543;
2. The input string is sorted in the above mentioned order, hence the number of times each character appears is exactly the size of the longest contiguous substring of the character;
3. No characters will appear more than 4 times;
4. X and D will appear no more than once each.

Test case:

3
X2AAKKKKQT9765433
DX22AKKQJTT884443
X2AAAKQQJT8554433


should have the output

yes
yes
no


Note:

• Output is not case sensitive, so YeS is also legal;
• You may assume an extra \n after the end of the input.

My attempt goes like this: Python, probably 84 85 Bytes

import re
input()
while n:=input():print("yes"if re.search(r"(.)\1{3}|DX",n)else"no")


Also, I have the following alternatives for line 3, while are approximately the same length:

while n:=input():print("yneos"[not re.search(r"(.)\1{3}|DX",n)::2])
while n:=input():print(["yes","no"][not re.search(r"(.)\1{3}|DX",n)])
while n:=input():print((not re.search(r"(.)\1{3}|DX",n))*"no"or"yes")


Is there anything I can do to make this shorter? Thanks in advance!

Source: Question made by a friend, problem ID on Luogu is P8466

• Your code seems to give "no" on the first test case: Try it online!. Can you check this?
– xnor
Aug 8 at 8:45
• @xnor yeah you’re right, I forgot to add r before my regex string, I’ll make an edit to add it, now it would be 85 bytes Aug 8 at 8:50
• A starting point for advice here is to not use re here. The import and re.search call are overkill what you're using them for, and they take too many bytes of overhead. Checking substrings or counting characters can be done with Python built-ins.
– xnor
Aug 8 at 9:01
• Can open(0) be used to take input here? May we terminate with error?
– xnor
Aug 8 at 9:27
• @JonathanAllan edited question to include source (the source is Luogu P8466, made by my friend) Aug 8 at 23:20

# Python, 72 bytes

for n in[*open(0)][1:]:print("yneos"[all(map(str.__ne__,' X '+n,n))::2])


Try it online!

Knocks 5 more bytes off of Jonathan Allan's answer.

• Instead of using 'X ' we can use ' X ' (extra space at start), then we wouldn’t have to slice the third argument, which is -3 Bytes. Aug 8 at 23:16
• There's a much different solution that gets 71 bytes, if trailing whitespace is acceptable. Aug 8 at 23:44

# 82 bytes

Can probably be golfed more, but here is a first golf:

-2 bytes by changing

"yes"if re.search(r"(.)\1{3}|DX",n)else"no"


To:

re.search(r"(.)\1{3}|DX",n)and"yes"or"no"


Try it online.

And another -1 by changing (not sure about this one, but both your 85 bytes version and this one gives an EOFError on TIO):

input()
while n:=input():...n


To:

n=input()
while n:...input()


Try it online.

• Thanks! I think EOFError is not okay (online judge says RE) so I guess my code would also be invalid :( ... However, I think using exec in a for loop would only be a +5. Sorry for not checking the online judge requirements, again thank you for your byte savings Aug 8 at 9:43
• "yneos"[cond::2] Aug 8 at 9:49
• @emanresuA I'm afraid that won't work with a regex-search object as cond: try it online. Unless I'm doing something wrong (which is pretty likely, since I'm not too skilled with Python). Aug 8 at 11:36
• @KevinCruijssen my first “alternative” in the question is utilising exactly that, note how I used not to force the match object to be casted to a bool (False), and when there’s no match, the None gets converted to a True (btw this works because bool is a subclass of int) if there’s a shorter way to get it converted, then it would probably golf off another few bytes Aug 8 at 13:02

# Python 3.8, 77 bytes

input()
while n:='X '+input():print("yneos"[all(map(str.__ne__,n,n[3:]))::2])


Try it online!

The check for four equal characters in a row can be achieved by checking for equality between a character and the character three positions to its right. This can be applied across a string with any(map(str.__eq__,n,n[3:]) which will yield True if so or False otherwise.

This is inverted in the code by checking that all such pairs of characters are not equal with all(map(str.__ne__,n,n[3:])) to allow the result to be used in a slice of "yneos" that steps by two characters. That is, "yneos"[False::2] is equivalent to "yneos"[0::2] which is "yes"; while "yneos"[True::2] is equivalent to "yneos"[1::2] which is "no".

To check for the presence of "DX" we only need to see if the string starts with "DX" due to the strict ordering and the rule that states each of "D" and "X" may only appear once. Moreover this means the string cannot have a second character of "X" unless it starts with "DX", so we actually only need to check if the second character is "X". Therefore prefixing the string with "X " allows a reuse of the check for equal characters being three apart for this too, hence n:='X '+input().