Count and Delete Each Substring Occurrence

Viewed 263

I'm writing a program which counts and then deletes the occurrence of a substring. Both these operations are done one at a time.

This however doesn't work for all cases:

Works Correctly:

string = abbab
substring = ab
Occurrences = 2

Doesn't Work:

string = abab
substring = ab
Occurrences: 1    # should be 2

Below is the code:

def count_delete(string, substring):
    occurrence = 0
    for i in range(len(string)):
        if string[i:i + len(substring)] == substring:
            occurrence += 1
            string = string.replace(substring, "", 1)
    return occurrence

def main():
    string = 'abbab'
    substring = 'ab'
    count = count_delete(string, substring)
    print("Occurrences:", count)

if __name__ == '__main__':
    main()

How do I get the correct output in all cases ?

5 Answers
    for i in range(len(string)): ***** 1
        if string[i:i + len(substring)] == substring: ***** 3
            occurrence += 1
            string = string.replace(substring, "", 1) ***** 2

Your bug is on the lines I marked.

On line ***** 2, you delete the substring you just found. Then on ***** 1, you will increment i. If substring immediately follows substring, you will not detect it, as your i variable has progressed.

I would suggest the following modification to make your existing code work:

  1. Find all instances of substring and mark the locations without modifying.
  2. Delete all substrings starting from the rear based on the locations marked in the previous step.

Alternatively, change ***** 3 to a while loop.

import re
string = 'abbcab'
substring = 'ab'

print(len(re.findall(r''+substring,string)))
#2

print(re.sub(r''+substring,'',string))
#bc

# Update: It seems  simple string manipulation  would do the task.
print(string.count(substring))
print(string.replace(substring,''))

Try this

def count_delete(string, substring):
    '''
    >>> print(count_delete('aabbcab', 'ab'))
    (2, 'abc')
    '''
    occurrence = 0
    i = 0
    while i < len(string):
        if string[i:i + len(substring)] == substring:
            occurrence += 1
            # string = string.replace(substring, "", 1) # replaces from index 0
            string = string[:i] + string[i+len(substring):]
            # substring was removed, so we keep i on the same position
        else:
            i += 1
            # substring not found, try next position
    return occurrence, string

This is only for training purposes, to understand the behavior of loops on iterables that are changed in-loop

The pythonic way is to count all and then replace all, or better, regex and capture all


this also works

def split_join(string, substring):
    '''
    >>> print(split_join('aabbcab', 'ab'))
    (2, 'abc')
    '''
    split = string.split(substring)
    return len(split) - 1, ''.join(split)

Since you throw away the reduced string and only report the number of occurrences, there's a much simpler and faster way to do that:

>>> 'abbab'.count('ab')
2
>>> 'abab'.count('ab')
2

This code may work better for you:

def count_del(string, substring):
    occurs = 0

    #using the find method to increment counter
    #if -1, no substring found, if 0 or >0, substring found
    #increment counter and search to the right of substring

    while string.find(substring) >= 0:
        string = string[string.find(substring) + len(substring):]

        #truncate all strings until substring + len(substring)
        #search only items in the right of substring

        occurs +=1
    return occurs

========= leaving my older code and comments as there is reference in the comments to this section.

Every time you go into the loop, you are decreasing the length of the string. That messes up your value of i in the if statement.

if string[i:i + len(substring)] == substring:

When you go into the loop for the first time, i is 0, therefore, your if statement converts to:

if string[0:0 + 2] == substring:

When you go into the loop for the second time, i is 1, therefore, your if statement converts to:

if string[1:1 + 2] == substring:

I don't think this is what you want to do. You still want to search from 0 instead of 1.

If you change your code as follows, it will give you the desired result

def count_delete(string, substring):
    occurrence = 0
    while len(string) >= len(substring):
    #for i in range(len(string)):   replaced for loop with while
        #check the string from beginning to len of substring
        if string[:len(substring)] == substring:
            occurrence += 1
            #string = string.replace(substring, "", 1)
        #instead of removing the entire substring, 
        #you may want to check if there are more occurrences of the same
        #to do that, use the below line

        string = string[1:]

        #this will find 2 'abab' in 'abababb' instead of 1
        #if thats what you want
    return occurrence
Related