我正在使用Python 3,我有两个字符串:abbcabb
和abca
。我想删除每个单个字符的重复出现。例如:
abbcabb
应该给出c
,而abca
应该给出bc
。
我尝试了以下正则表达式(here):
(.)(.*?)\1
但是,它给第一个字符串输出错误。另外,当我尝试另一个(here)时:
(.)(.*?)*?\1
但是,这一次又给出了错误的输出。怎么了?
python代码是一个打印语句:
print(re.sub(r'(.)(.*?)\1', '\g<2>', s)) # s is the string
答案 0 :(得分:3)
无需正则表达式即可解决问题,如下所示
>>>''.join([i for i in s1 if s1.count(i) == 1])
'bc'
>>>''.join([i for i in s if s.count(i) == 1])
'c'
答案 1 :(得分:2)
re.sub()
不执行重叠替换。替换第一个比赛后,它将开始寻找比赛结束。因此,当您在
abbcabb
它首先将abbca
替换为bbc
。然后它将bb
替换为空字符串。它不会返回并在bbc
中寻找另一个匹配项。
如果需要,您需要编写自己的循环。
while True:
newS = re.sub(r'(.)(.*?)\1', r'\g<2>', s)
if newS == s:
break
s = newS
print(newS)
答案 2 :(得分:2)
正则表达式似乎不是理想的解决方案
我喜欢this answer,但是在列表理解中反复使用count
每次都会遍历所有元素。
无需正则表达式且没有O(n**2)
复杂性,只需O(n)
使用collections.Counter
即可解决
像这样:
import collections
s = "abbcabb"
cnt = collections.Counter(s)
s = "".join([c for c in s if cnt[c]==1])
(作为奖励,您可以更改计数以保留具有2、3的字符,无论出现什么情况)
答案 3 :(得分:1)
编辑:基于评论交换-如果您仅关注字母计数的均等性,那么您不希望使用正则表达式,而是希望使用@jon的推荐方法。 (如果您不关心顺序,那么使用较长字符串的性能更高的方法可能会使用collections.Counter
之类的东西。)
关于您要匹配的内容,我的最佳猜测是:“一个或多个字符-称为此子模式A-随后是一组不同的一个或多个字符-称为此子模式B-再次是子模式A”
您可以将+
用作“一个或多个”的快捷方式(而不是一次指定,然后在其余匹配中使用*
),但是无论哪种方式,您都需要获取子模式正确。让我们尝试一下:
>>> import re
>>> pattern = re.compile(r'(.+?)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'bbcbaca'
嗯。那没用。为什么?因为第一个模式不是贪婪的,所以我们的“子模式A”只能匹配字符串中的第一个a
-毕竟确实出现了。因此,如果我们使用贪婪的匹配,Python将会回溯,直到找到与子模式A一样长的模式,而该模式仍然允许A-B-A模式出现:
>>> pattern = re.compile(r'(.+)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'cbc'
对我很好。
答案 4 :(得分:0)
网站对此进行了很好的解释,将鼠标悬停并使用“说明”部分。
(.)(.*?)\1
不会删除或匹配所有重复出现的事件。它匹配1个字符,其后是中间的任何东西,直到再次遇到相同的字符为止。
因此,对于abbcabb
,“三明治”部分应位于两个bbc
之间的a
编辑: 您可以尝试不使用正则表达式的方法,如下所示:
string = "abbcabb"
result = []
for i in string:
if i not in result:
result.append(i)
else:
result.remove(i)
print(''.join(result))
请注意,这会产生字符串的“最后”奇数次出现,而不是首先出现。
对于“首次”已知发生,您应使用此answer中建议的计数器。只需更改条件以检查奇数。 pseudo code(count[letter] %2 == 1)