统计和删除每个子字符串出现的次数

3

我正在编写一个程序,用于计算并删除子字符串的出现次数。这两个操作都是逐个执行的。

然而,并非所有情况下都适用:

正确工作:

string = abbab
substring = ab
Occurrences = 2

不起作用:

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

以下是代码:
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()

我如何在所有情况下都获得正确的输出?


1
如果你不打算返回 string,为什么要修改它呢?将替换行移动到 for 循环之后,并通过删除 1 参数来替换所有出现的值。 - RichieV
@RichieV 根据问题陈述,计数和删除必须按照连续的方式进行。 - Saurabh
请提供问题陈述的链接。 - superb rain
1
@RichieV 那么它将计算重叠的出现次数。 - superb rain
1
我认为人们没有注意以下用例。用例1:字符串:aaa,模式:aa,结果:a。用例2:字符串:ababa,模式aba,结果:ba。先读后删可以工作,只要在读取时跳过i(即如果找到,则 i + = pattern.length - 1)以避免检测重叠的匹配,但不能使用诸如findAll之类的库函数。 - John
5个回答

1
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,''))

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

你的错误出现在我标记的行上。

在第 ***** 2 行,你删除了刚刚找到的子字符串。然后在第 ***** 1 行,你会增加 i。如果子字符串紧随其后,由于 i 变量已经前进,你将无法检测到它。

我建议对现有代码进行以下修改:

  1. 查找所有子字符串实例并标记位置,不要进行修改。
  2. 根据上一步中标记的位置,从后面开始删除所有子字符串。

或者,将 ***** 3 改为 while 循环。


谢谢您指出这个错误。我已经将if改为了while循环。 - Saurabh
使用上述代码对于字符串 abababb,将会得到 occurrence = 2 的结果。问题在于你在 if语句 中使用了 i。你应该使用 [:len(substring)] 而不是 [i:i+len(substring)]。我没有投反对票,但请你纠正代码。这段代码将无法提供正确的结果。 - Joe Ferndz
对于 string=abababbsubstring=ab,我得到了 Occurrences: 3,感谢 @John 的建议,我已将 if 改为 while - Saurabh
2
代码是原帖作者的复制/粘贴代码,并标记了行号。我没有对原始代码进行任何修改,只是粘贴在那里以便参考行号,所以没有什么需要更正的地方。一旦原帖作者更改了他运行的代码,就不会遇到连续模式检测的问题。 - John
@John,抱歉,我没有意识到你在指的是操作码。@Saurabh,如果你尝试了原始代码,string = abababbsubstring = ab将会得到Occurrences: 2 - Joe Ferndz
@JoeFerndz 是的,那就是bug。 - Saurabh

1

试试这个

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

这仅用于培训目的,以了解在循环中更改的可迭代对象的行为。
Pythonic 的方法是计算所有并替换所有,或者更好的方法是使用正则表达式并捕获所有。
这也可以工作。
def split_join(string, substring):
    '''
    >>> print(split_join('aabbcab', 'ab'))
    (2, 'abc')
    '''
    split = string.split(substring)
    return len(split) - 1, ''.join(split)

你不应该从 i 开始搜索字符串,因为你正在修剪字符串。这个逻辑是不正确的。请查看我的答案以获取解释。 - Joe Ferndz
@superb 谢谢你指出这一点,.replace 是从索引0开始查找第一个出现的匹配项,在这种情况下,通过在先前的迭代中删除“子字符串”,形成了新的匹配项。 - RichieV

0

由于您丢弃了缩小后的字符串并仅报告出现次数,因此有一种更简单和更快的方法可以实现:

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

@John 它会产生1。你为什么不试一下呢?这比你的评论要少得多。 - superb rain

0

这段代码可能对你更有效:

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

========= 在代码和注释中引用了这一部分,因此保留了旧代码和注释。

每次进入循环时,都会减少字符串的长度。这会破坏你在 if 语句中的 i 的值。

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

当你第一次进入循环时,i0,因此,你的 if 语句转换为:

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

当你第二次进入循环时,i 是 1,因此,你的 if 语句转换为:

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

我认为这不是你想做的。你仍然想从 0 开始搜索,而不是从 1 开始。

如果你按照以下方式更改代码,它将给你所需的结果。

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

你可以将 #string = string.replace(substring, "", 1) 替换为 string = string[len(substring):],这样可以得到相同的结果。 - Joe Ferndz
你提出了一个不同的问题解释,也是正确的……然而,你的代码会无限循环,除非字符串恰好是子字符串的整数倍(例如,“abababc”/“ab”将停留在“c”上而永远无法跳出)。 - RichieV
@RichieV,谢谢。我没听清楚,我的错。谢谢。代码已修复。此外,如果问题只是要找到字符串的出现次数,最简单的方法是计数。 - Joe Ferndz
你的修改仍然没有在字符串 abababb 中断开子串 b... 我同意,但我认为这个练习的目的是提高同时循环+修改可迭代对象的风险意识。 - RichieV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接