有效地检查一个字符串是否包含另一个字符串的所有字符

时间:2016-11-12 21:29:51

标签: python performance

我有两个字符串:一个字一个字,另一个字母拼写字。我想知道这些争夺的字母是否有足够的字母来拼写这个单词。我已经提出了一个算法来做到这一点,但它不够有效,我希望我能得到一些帮助,使它更快。

这是我到目前为止所拥有的:

s1 = 'hypochondriac'
s2 = 'yqhpwoewnqlchpijcdrxpoa'

temp = list(s1)
for X in s2:
    for Y in temp:
        if X == Y:
            temp.remove(X)
            X = '@'
if temp == []:
    print('Found ', s1)

我有一个问题,一旦X匹配我需要增加X但我不知道如何通过使它成为at符号将其从等式中取出。我尝试过使用break但是它没有达到足以打破s2循环的程度。无论哪种方式,我都非常确定这种双循环理念与具有一定经验的人相比会非常缓慢。有什么想法吗?

4 个答案:

答案 0 :(得分:3)

您的代码效率不高,因为您在双循环中进行迭代。对于s1中的每个字母,在最坏的情况下(无匹配),您将遍历所有s2

使用Counter object代替;这些充当多集,您可以在O(1)时间内测试角色是否存在并管理剩余计数:

from collections import Counter

def contains(s1, s2):
    s2set = Counter(s2)
    for c in s1:
        count = s2set[c]
        if not c:
            return False
        if count == 1:
            del s2set[c]
        else:
            s2set[c] = count - 1
    return True

您也可以将s1转换为多套装,并检查s2的多套装是否包含每个条目的足够字母:

def contains(s1, s2):
    s1set = Counter(s1)
    s2set = Counter(s2)
    for c, count in s1set.items():
        if count > s2set[c]:
            return False
    return True

后者可以使用all() function进一步缩小,如果传递的任何结果为False,则提前FalseTrue否则:{/} >

def contains(s1, s2):
    s2set = Counter(s2)
    return all(count <= s2set[c] for c, count in Counter(s1).items())

在所有这些中,您只需迭代s1s2 一次(直接或生成多套)。

后者的演示:

>>> from collections import Counter
>>> def contains(s1, s2):
...     s2set = Counter(s2)
...     return all(count <= s2set[c] for c, count in Counter(s1).items())
...
>>> s1 = 'hypochondriac'
>>> s2 = 'yqhpwoewnqlchpijcdrxpoa'
>>> contains(s1, s2)
True
>>> contains(s1 + 'b', s2)
False

答案 1 :(得分:2)

扩展@Martijn_Pieters解决方案,您可以这样使用Counter

from collection import Counter
def contains(s1, s2):
    c1, c2 = Counter(s1), Counter(s2)
    return all(c1[c] <= c2[c] for c in s1)

如果Counter[key]不存在,您可以依赖key默认返回0的事实。

答案 2 :(得分:0)

反过来做。从s2

中删除字符
s1 = 'hypochondriac'
s2 = 'yqhpwoewnqlchpijcdrxpoa'

temp = list(s2)
try:
    for ch in s1:
        temp.remove(ch)
except ValueError:
    print("not found")
else:
    print("found", s1)

答案 3 :(得分:0)

这是使用NumPy -

的矢量化方法
import numpy as np

def in_string(s1,s2):
    arr1 = np.fromstring(s1, dtype=np.uint8)
    arr2 = np.fromstring(s2, dtype=np.uint8)
    return np.in1d(arr1,arr2).all()

示例运行 -

In [50]: in_string('hypochondriac','yqhpwoewnqlchpijcdrxpoa')
Out[50]: True

# Let's add in a `z` at the end of first word which isn't in the scramble
In [51]: in_string('hypochondriacz','yqhpwoewnqlchpijcdrxpoa')
Out[51]: False

这是另一个使用np.searchsorted -

的基于NumPy的人
def in_string_v2(s1,s2):
    arr1 = np.fromstring(s1, dtype=np.uint8)
    arr2 = np.fromstring(s2, dtype=np.uint8)
    u1 = np.unique(arr1)
    u2 = np.unique(arr2)
    return ~(np.searchsorted(u2,u1) == np.searchsorted(u2,u1,'right')).any()

这是另一个一次处理一个单词列表的单词列表 -

def in_string_v3(list_s1,s2):
    l_arr1 = np.fromstring("".join(list_s1), dtype=np.uint8)
    arr2 = np.fromstring(s2, dtype=np.uint8)
    lens = np.array(map(len,list_s1))
    comp_lens = np.in1d(l_arr1,arr2).cumsum()[lens.cumsum()-1]
    calc_lens = np.append(comp_lens[0],comp_lens[1:]-comp_lens[:-1])
    return lens == calc_lens

示例运行 -

In [185]: ls1 = ['hypochondriac','hypochondriachsdhsahdsadhsa','hihfheifheozz']

In [186]: s2 = 'yqhpwoewnqlchpijcdrxpoadjksdgdkjsfkbdsfbdsdsaduiawyei'

In [187]: in_string_v3(ls1,s2)
Out[187]: array([ True,  True, False], dtype=bool)

以另一种方式处理单词列表 -

def in_string_v4(list_s1,s2):
    l_arr1 = np.fromstring("".join(list_s1), dtype=np.uint8)
    arr2 = np.fromstring(s2, dtype=np.uint8)
    lens = np.array(map(len,list_s1))
    clens = lens.cumsum()
    non_matching_idx = np.nonzero(~np.in1d(l_arr1,arr2))[0]
    non_matching_grp = np.unique(clens.searchsorted(non_matching_idx))
    return ~np.in1d(np.arange(len(list_s1)),non_matching_grp)