6
\$\begingroup\$

Here is my problem:

Return the number of times that the string code appears anywhere in the given string, except we'll accept any letter for the d, so cope and cooe count.

I solved it by creating a list with all 26 possible co_e combinations and then used two for loops to iterate through the list and search for matches in the given string.

Is there another way I could have created that list? or is there a way to ignore the third character in word and only search for matching characters 1, 2 and 4?

Here is my solution:

if len(inputstring)<4:
    return 0
wordlist=['coae','cobe','coce','code'...etc]
count=0

for i in wordlist:
    for j in range(len(inputstring)):
        if inputstring[j:j+4] == i:
            count+=1
return count
\$\endgroup\$
4
  • 1
    \$\begingroup\$ @MartijnPieters I realize you did this on the SO question, but just so no one gets the wrong idea on this site: Do not edit a questioner's code with improvements. If you have improvements to suggest, post an answer. \$\endgroup\$
    – anon
    Commented Dec 27, 2017 at 22:55
  • \$\begingroup\$ @QPaysTaxes yup, it’s relatively common to do so on SO, and I did do before deciding on migration. I’m aware you shouldn’t do so here, feel free to roll it back. \$\endgroup\$ Commented Dec 27, 2017 at 22:57
  • 1
    \$\begingroup\$ @MartijnPieters I don't see any harm in leaving it; I just don't want people seeing the comment from another site and assuming it's okay here, too. \$\endgroup\$
    – anon
    Commented Dec 27, 2017 at 23:01
  • \$\begingroup\$ Right, comment nuked. \$\endgroup\$ Commented Dec 27, 2017 at 23:06

3 Answers 3

6
\$\begingroup\$

Just use regular expressions!

import re
# creating regular expression with "any" letter at 3 position
r = re.compile("co[a-z]e")
# and taking all the matches:
matches = r.findall("some of my code, nope to come, cofe and cole, or cope and dome, rope and hope!")
count = len(matches)
print(count)
\$\endgroup\$
4
  • \$\begingroup\$ print(len(matches or [])). \$\endgroup\$
    – CristiFati
    Commented Dec 27, 2017 at 11:48
  • \$\begingroup\$ You right! the count was needed! \$\endgroup\$
    – MihanEntalpo
    Commented Dec 27, 2017 at 11:51
  • \$\begingroup\$ findall() always returns a list, there is no need to use or here. len(matches) will always work. \$\endgroup\$ Commented Dec 27, 2017 at 12:00
  • \$\begingroup\$ It's true, my bad :) \$\endgroup\$ Commented Dec 27, 2017 at 12:03
2
\$\begingroup\$

You can use in to test if a substring appears:

>>> 'code' in 'recoded'
True

This removes the need to loop over the string.

You can generate your test strings by looping over all letters in the alphabet; Python already has the latter available for you at string.ascii_lowercase and a list comprehension:

import string

possibilities = ['co{l}e'.format(l) for l in string.ascii_lowercase]

count = 0
for possibility in possibilities:
    if possibility in inputstring:
        count += 1

You could also just test for co appearing, and see if there is a letter e further along. You can use the str.find() method to find the position of an occurrence and search from there; str.find() takes a starting position to search for the next match:

count = 0
start = 0
while True:
    position = inputstring.find('co')
    if position == -1:
        # not found, end the search
        break
    if len(inputstring) > position + 2 and inputstring[position + 2] == 'e':
        count += 1
    start = position + 1

However, most experienced programmers will use regular expressions to find such matches:

import re

count = len(re.findall(r'co[a-z]e', inputstring))

Here the expression uses [a-z] to match a single character as a class, anything in that series (so letters from a to z) would match. The re.findall() function returns a list of all matches found in the input string, so all you have to do then is take the len() length of that list to get a count.

\$\endgroup\$
6
  • \$\begingroup\$ ['co{l}e'.format(l) for l in string.ascii_lowercase] - possible, regular expression engine make something like this under the hood when you use "co[a-z]e" pattern :) \$\endgroup\$
    – MihanEntalpo
    Commented Dec 27, 2017 at 11:49
  • 2
    \$\begingroup\$ @MihanEntalpo: a little patience please, I'll get there too! Take into account this user is probably still in the early stages of learning to program, and regular expressions can be quite challenging. \$\endgroup\$ Commented Dec 27, 2017 at 11:52
  • \$\begingroup\$ "This removes the need to loop over the string." Strictly speaking, it doesn't remove the loop; you just don't need to do it manually, and there might be some cool optimizations done (for example, if it matches the first three letters of code, then you can skip over those three, since o won't match c) \$\endgroup\$
    – anon
    Commented Dec 27, 2017 at 22:57
  • 1
    \$\begingroup\$ Right, it removes the need to manually loop in Python. The C implementation of the containment search is based on Boyer-Moore; the algorithm does skip in larger steps where possible. \$\endgroup\$ Commented Dec 27, 2017 at 23:03
  • 1
    \$\begingroup\$ And yes, a generator expression would be slightly more efficient (certainly in terms of memory, less so in terms of speed). I answered with a focus on teaching a beginner however, not with a focus on code review, so I ignored that option for simplicity’s sake. \$\endgroup\$ Commented Dec 27, 2017 at 23:06
0
\$\begingroup\$

Simple and beautiful. I hope it is what you want

import re

list=['coae','cobe','coce','code','coco']
count=0

for i in list:
  if re.match(r'co[a-z]e', i):
    count+=1

print(count)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.