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\$

migrated from stackoverflow.com Dec 27 '17 at 11:57

This question came from our site for professional and enthusiast programmers.

  • 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\$ – Nic Hartley Dec 27 '17 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\$ – Martijn Pieters Dec 27 '17 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\$ – Nic Hartley Dec 27 '17 at 23:01
  • \$\begingroup\$ Right, comment nuked. \$\endgroup\$ – Martijn Pieters Dec 27 '17 at 23:06
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\$
  • \$\begingroup\$ print(len(matches or [])). \$\endgroup\$ – CristiFati Dec 27 '17 at 11:48
  • \$\begingroup\$ You right! the count was needed! \$\endgroup\$ – MihanEntalpo Dec 27 '17 at 11:51
  • \$\begingroup\$ findall() always returns a list, there is no need to use or here. len(matches) will always work. \$\endgroup\$ – Martijn Pieters Dec 27 '17 at 12:00
  • \$\begingroup\$ It's true, my bad :) \$\endgroup\$ – MihanEntalpo Dec 27 '17 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\$
  • \$\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 Dec 27 '17 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\$ – Martijn Pieters Dec 27 '17 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\$ – Nic Hartley Dec 27 '17 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\$ – Martijn Pieters Dec 27 '17 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\$ – Martijn Pieters Dec 27 '17 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, privacy policy and cookie policy

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