检查组合字符串中子串顺序的算法

时间:2012-09-23 22:10:46

标签: python string algorithm

问题如下:如果有两个字符串str1str2以及另一个字符串str3,请编写一个函数来检查str3是否包含str1 str2 1}}字母和def isinter(str1,str2,str3): p1,p2,p3 = 0,0,0 while p3 < len(str3): if p1 < len(str1) and str3[p3] == str1[p1]: p1 += 1 elif p2 < len(str2) and str3[p3] == str2[p2]: p2 += 1 else: break p3 = p1+p2 return p3 == len(str3) 的字母与原始序列中的字母顺序相同,但它们可能是交错的。因此, adbfec 对于子字符串 adf bec 返回true。我在Python中编写了以下函数:

{{1}}

此程序的另一个版本位于ardentart(最后一个解决方案)。现在哪一个更好?我想我的,因为它可能在线性时间内完成。无论是否更好,我的算法还有进一步优化的空间吗?

4 个答案:

答案 0 :(得分:2)

不幸的是,您的版本无效。想象一下输入 abacacab。您的算法返回False,这是不正确的。

问题是当str1中看到的字母可以双向解释时,你总是走str3;步行可能需要str2,但它与你的算法没有同等的机会。

答案 1 :(得分:1)

另一种接近它的方法是使用python的regex模块。您可以拆分str1的字符,并用。*包围每个字符以匹配它们之间的任何数字(或无)字符。这将为您提供匹配str1的模式。然后对str2执行相同操作,然后只运行re.match(str1pattern,str3)和re.match(str2pattern,str3)。如果它们都返回对象(即除了None之外的任何东西),那么你就可以匹配两个字符串。

这可能会更好地扩展,因为它更容易添加更多字符串来检查,如果你需要更好的性能来搜索各种其他字符串,那么你也可以编译模式。

答案 2 :(得分:1)

您可以在列表中拆分所有三个字符串:

list1 = list(str1)

然后使用您现在使用的相同算法行走list3,检查list3[i]是否等于list1[0]list2[0]。如果是,您需要del相应列表中的项目。

然后可以将过早的列表结束作为例外捕获。

算法完全相同,但实现应该更高效。

UPDATE :事实证明它实际上不是(大约是时间的两倍)。哦,知道可能有用。

在对不同场景进行基准测试时,事实证明除非指定三个字符串长度是“精确”(即len(p1)+ len(p2)== len(p3)),否则最有效优化是检查第一件事。这会立即丢弃由于字符串长度不正确而导致两个输入字符串无法与匹配的所有情况。

然后我遇到一些情况,两个字符串中都有相同的字母,并将其分配给list1或list2可能会导致其中一个字符串不再匹配。在这些情况下,算法失败并出现假阴性,这需要递归。

def isinter(str1,str2,str3,check=True):
    # print "Checking %s %s and %s" % (str1, str2, str3)
    p1,p2,p3 = 0,0,0
    if check:
        if len(str1)+len(str2) != len(str3):
            return False
    while p3 < len(str3):
        if p1 < len(str1) and str3[p3] == str1[p1]:
            if p2 < len(str2) and str3[p3] == str2[p2]:
                # does str3[p3] belong to str1 or str2?
                if True == isinter(str1[p1+1:], str2[p2:], str3[p3+1:], False):
                   return True
                if True == isinter(str1[p1:], str2[p2+1:], str3[p3+1:], False):
                   return True
                return False
            p1 += 1
        elif p2 < len(str2) and str3[p3] == str2[p2]:
            p2 += 1
        else:
            return False
        p3 += 1
    return p1 == len(str1) and p2 == len(str2) and p3 == len(str3)

然后我在随机字符串上运行了一些基准测试,这就是仪器(注意它总是生成有效 shuffles,这可能会产生有偏见的结果):

for j in range(3, 50):
        str1 = ''
        str2 = ''
        for k in range(1, j):
                if random.choice([True, False]):
                        str1 += chr(random.randint(97, 122))
                if random.choice([True, False]):
                        str2 += chr(random.randint(97, 122))
        p1 = 0
        p2 = 0
        str3 = ''
        while len(str3) < len(str1)+len(str2):
                if p1 < len(str1) and random.choice([True, False]):
                        str3 += str1[p1]
                        p1 += 1
                if p2 < len(str2) and random.choice([True, False]):
                        str3 += str2[p2]
                        p2 += 1
        a = time.time()
        for i in range(1000000):
                isShuffle2(str1, str2, str3)
        a = (time.time() - a)
        b = time.time()
        for i in range(1000000):
                isinter(str1, str2, str3)
        b = (time.time() - b)

        print "(%s,%s = %s) in %f against %f us" % (str1, str2, str3, a, b)

结果似乎表明缓存+ DP算法对字符串的效率更高。当字符串变长(超过3-4个字符)时,缓存+ DP算法开始失去理由。在大约10长度,上面的算法执行的速度是完全递归的缓存版本的两倍。

如果字符串包含重复字符(我通过将范围从a-z限制为a-i)并且重叠是轻微的,则DP算法执行得更好,但仍然比上面的差。例如,在这种情况下,DP仅损失2us:

(cfccha,ddehhg = cfcchaddehhg) in 68.139601 against 66.826320 us

毫不奇怪,完全重叠(每个字符串依次为一个字母)看到的差异较大,比例高达364:178(略大于2:1)。

答案 3 :(得分:0)

首先,只是一个实现点:我认为你可以摆脱对str1和str2长度的测试。在C中,字符串以nul字符终止,因此在str3中永远不会找到此特殊字符。如果找到正确的字符,只需添加p1++即可。但是在python中我不记得这个功能是不是...抱歉,我不是一个严肃的python用户。如果p1 == len(p1)?

,str1 [p1]的输出是多少?

除此之外,正如Jirka Hanika所指出的,您的代码输出是错误的。我已经看到了另一种失败的情况:如果一个字符与两个子字符串相同。例如:如果str1 =“abc”,str2 =“dbe”,则str3 =“adbec”包含str1和str2,但您的算法在这种情况下失败。问题来自elif语句,而是放入另一个if。

Iserni的代码输出在我看来是正确的。