在字符串中查找“一个出现两次的字母”

Posted

技术标签:

【中文标题】在字符串中查找“一个出现两次的字母”【英文标题】:Find "one letter that appears twice" in a string 【发布时间】:2016-03-19 14:21:12 【问题描述】:

我正在尝试使用 RegEx(或者也许有更好的方法?)来捕捉一个字母是否在字符串中出现两次,例如我的字符串是:

ugknbfddgicrmopn

输出将是:

dd

但是,我尝试过类似的方法:

re.findall('[a-z]2', 'ugknbfddgicrmopn')

但在这种情况下,它会返回:

['ug', 'kn', 'bf', 'dd', 'gi', 'cr', 'mo', 'pn']   # the except output is `['dd']`

我也有办法得到期望的输出:

>>> l = []
>>> tmp = None
>>> for i in 'ugknbfddgicrmopn':
...     if tmp != i:
...         tmp = i
...         continue
...     l.append(i*2)
...     
... 
>>> l
['dd']
>>> 

但这太复杂了……


如果是'abbbcppq',则只捕获:

abbbcppq
 ^^  ^^

所以输出是:

['bb', 'pp']

那么,如果是'abbbbcppq',抓bb两次:

abbbbcppq
 ^^^^ ^^

所以输出是:

['bb', 'bb', 'pp']

【问题讨论】:

你可以使用反向引用,([a-z])\1 你似乎期待——但没有提到——连续性,如果"ddd"在场,你没有解释你想要什么结果。 @kevin 如果字母出现两次以上怎么办>? 你的意思是字母出现两次一起还是输入中的任何地方 非常相似[python]: use re to find consecutively repeated chars 【参考方案1】:

您需要使用基于捕获组的正则表达式并将您的正则表达式定义为原始字符串。

>>> re.search(r'([a-z])\1', 'ugknbfddgicrmopn').group()
'dd'
>>> [i+i for i in re.findall(r'([a-z])\1', 'abbbbcppq')]
['bb', 'bb', 'pp']

>>> [i[0] for i in re.findall(r'(([a-z])\2)', 'abbbbcppq')]
['bb', 'bb', 'pp']

注意,re.findall 这里应该返回元组列表,其中第一组匹配的字符作为第一个元素,第二组匹配的字符作为第二个元素。对于我们的案例,第一组中的字符就足够了,所以我提到了i[0]

【讨论】:

Ohhhhh,重新阅读答案后,我了解它现在是如何工作的。所以([a-z]) 抓住第一个字母,然后\1 重复它。 :) @KevinGuan ya,确切地说.. () 称为捕获组。所以([a-z]) 捕获第一个字母,下面的\1 是对第一个捕获组的反向引用。所以\1指的是第一组匹配的所有字符。【参考方案2】:

作为 Pythonic 方式,您可以在列表理解中使用 zip 函数:

>>> s = 'abbbcppq'
>>>
>>> [i+j for i,j in zip(s,s[1:]) if i==j]
['bb', 'bb', 'pp']

如果您正在处理大字符串,您可以使用iter() 函数将字符串转换为迭代器并使用itertols.tee() 创建两个独立的迭代器,然后在第二个迭代器上调用next 函数消耗第一项并使用此迭代器调用 zip 类(在 Python 2.X 中使用返回迭代器的 itertools.izip())。

>>> from itertools import tee
>>> first = iter(s)
>>> second, first = tee(first)
>>> next(second)
'a'
>>> [i+j for i,j in zip(first,second) if i==j]
['bb', 'bb', 'pp']

RegEx 配方为基准:

# ZIP
~ $ python -m timeit --setup "s='abbbcppq'" "[i+j for i,j in zip(s,s[1:]) if i==j]"
1000000 loops, best of 3: 1.56 usec per loop

# REGEX
~ $ python -m timeit --setup "s='abbbcppq';import re" "[i[0] for i in re.findall(r'(([a-z])\2)', 'abbbbcppq')]"
100000 loops, best of 3: 3.21 usec per loop

在您最后一次编辑后,如评论中所述,如果您只想匹配"abbbcppq" 等字符串中的一对b,您可以使用finditer(),它返回匹配对象的迭代器,并使用@987654334 提取结果@方法:

>>> import re
>>> 
>>> s = "abbbcppq"
>>> [item.group(0) for item in re.finditer(r'([a-z])\1',s,re.I)]
['bb', 'pp']

请注意,re.IIGNORECASE 标志,它使 RegEx 也匹配大写字母。

【讨论】:

好吧,作为我的编辑,我想要来自abbbcbb。好的,我知道这是我的另一个示例的简短版本,我的输出示例不是我的编辑所期望的……对此感到抱歉…… @Kasramvd:性能不是一切,在这种情况下很可能无关紧要。正则表达式是解决问题的工具,它们清晰简洁地解决了这个问题。 在分析程序并确定哪些部分是瓶颈时,性能很重要。在这种情况下,绝对没有理由担心性能。因此,可读性应该是主要因素,在这种情况下这是完全主观的。 那个。因为这里没有理由使用正则表达式。 @njzk2:一个很好的理由是,这并不能满足 OP 对字符串 abbbc 的要求(例如,['bb'] 用于正则表达式,['bb', 'bb'] 用于此代码)。【参考方案3】:

使用反向引用,很简单:

import re
p = re.compile(ur'([a-z])\11,')
re.findall(p, u"ugknbfddgicrmopn")
#output: [u'd']
re.findall(p,"abbbcppq")
#output: ['b', 'p']

更多细节可以参考perl中类似的问题:Regular expression to match any character being repeated more than 10 times

【讨论】:

\11, 会写成\1+【参考方案4】:

没有正则表达式很容易:

In [4]: [k for k, v in collections.Counter("abracadabra").items() if v==2]
Out[4]: ['b', 'r']

【讨论】:

嗯...如果输入为abbbbcppq,则不起作用。也许问题是因为if v == 2 :) 您的问题有点模棱两可:我们是在寻找所有出现多次的字母,还是只寻找在整个输入中出现两次的字母?这个答案对后者来说是准确的,但对于前者[k for k, v in collections.Counter("abbbbcppq").items() if v>1] 就可以了。【参考方案5】:

也许你可以使用生成器来实现这一点

def adj(s):
    last_c = None
    for c in s:
        if c == last_c:
            yield c * 2
        last_c = c

s = 'ugknbfddgicrmopn'
v = [x for x in adj(s)]
print(v)
# output: ['dd']

【讨论】:

【参考方案6】:

“或者也许有一些更好的方法”

由于正则表达式经常被下一个遇到您的代码的开发人员(甚至可能是您)误解, 而且由于更简单!= 更短,

下面的伪代码怎么样:

function findMultipleLetters(inputString)         
    foreach (letter in inputString) 
        dictionaryOfLettersOccurrance[letter]++;
        if (dictionaryOfLettersOccurrance[letter] == 2) 
            multipleLetters.add(letter);
        
    
    return multipleLetters;

multipleLetters = findMultipleLetters("ugknbfddgicrmopn");

【讨论】:

【参考方案7】:
A1 = "abcdededdssffffccfxx"

print A1[1]
for i in range(len(A1)-1):
    if A1[i+1] == A1[i]:
        if not A1[i+1] == A1[i-1]:
            print A1[i] *2

【讨论】:

欢迎来到 SO!回答的时候还要加上代码说明。 在这种情况下,如果我有ffff,那么输出将是['dd', 'ss', 'ff', 'ff', 'ff'] 其实...这仍然没有赶上我在 cmets 中所说的 'ff', 'ff'【参考方案8】:
>>> l = ['ug', 'kn', 'bf', 'dd', 'gi', 'cr', 'mo', 'pn']
>>> import re
>>> newList = [item for item in l if re.search(r"([a-z]1)\1", item)]
>>> newList
['dd']

【讨论】:

如果你给一个物品清单有什么用?这不适用于其他字符串。 我使用了re.search,它只适用于字符串。 另外,它也适用于其他字符串。就像您在列表中添加项目“zz”一样,它会同时给出“dd”和“zz”。 我的意思是你提供了一个预定义的列表。所以它会从列表中匹配,并且你已经给出了所有长度为 2 的列表项。所以你的程序不灵活。给定一个字符串,它不会给出所需的输出。我只是说输入的形式是String 而不是list

以上是关于在字符串中查找“一个出现两次的字母”的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 2351. 第一个出现两次的字母

算法leetcode6124. 第一个出现两次的字母(rust和go重拳出击)

算法leetcode6124. 第一个出现两次的字母(rust和go重拳出击)

LeetCode第 303 场周赛

LeetCode第 303 场周赛

LeetCode第 303 场周赛