Source

Part 1: A ‘nice’ string contains at least three vowels, one double letter (such as xx), and none of the strings ab, cd, pq, or xy. Count nice strings.

def is_nice(word):
return (
re.search(r'.*([aeiou].*){3}', word)
and re.search(r'(.)\1', word)
and not re.search(r'(ab|cd|pq|xy)', word)
)

nice_count = 0
for line in sys.stdin:
if is_nice(line.strip()):
nice_count += 1

print(nice_count)

This is a perfect application for regular expressions. In this case, .*([aeiou].*){3} says to match a vowel [aeiou] followed by any number (including zero) three times. (.)\1 says to match any one character, then match that same character again (\1 refers to the first matched group in parenthesis). (ab|cd|pq|xy) matches any one of the verboten sequences and then we negate it with not.

Part 2: A ‘nice’ string now contains a pair of letters appearing twice in a string (such as xy in axybcxydef) and one pattern of the form xyx (with the first and third letters the same).

The code is the same, just change the regular expressions to two new patterns, each with a back reference:

re.search(r'(..).*\1', word)
and re.search(r'(.).\1', word)

The first matches any two characters followed by any number (including zero) uninteresting characters, followed by that same first two characters. The second does the same, only with a single character repeated group and exactly one random character in between.