用Python 2.7正则实现最长子序列单词查找,咨询边界案例
首先得说,用.*拼接单词字符生成正则来匹配子序列的思路真的很聪明,对于示例输入也能得到正确结果,但在一些特殊场景下可能会出问题,或者需要额外处理:
1. 多个等长的符合条件的单词
如果D里存在多个长度相同且都是S子序列的单词,你的代码返回的结果是不确定的。因为Python 2.7里字典的键是无序的(Python 3.7+才保证插入顺序),max(in_dict, key=in_dict.get)会返回字典遍历顺序中第一个遇到的最长单词,而这个顺序是不可控的。比如:
S = "abcde" D = {"ace", "ade"}
这两个单词都是S的子序列且长度相同,你的代码可能返回"ace"也可能返回"ade",取决于字典的内部顺序。如果需要确定的结果(比如返回字典序最小的),可以改成:
max_len = max(in_dict.values()) candidates = [word for word, length in in_dict.items() if length == max_len] return min(candidates)
2. 单词包含正则特殊字符
如果D里的单词包含., *, +这类正则元字符,你的代码会出现错误匹配。比如:
S = "axxb" D = {"a.b"}
你的代码生成的正则是a.*.*b,这个正则会匹配axxb(因为.在正则里匹配任意字符),但实际上a.b并不是axxb的子序列(S里没有.字符)。解决这个问题需要对每个字符做正则转义:
pattern = ".*".join(re.escape(c) for c in word)
3. 空字符串的情况
如果D里包含空字符串,它会被判定为S的子序列(因为re.search("", S)永远返回True)。如果业务逻辑里不需要考虑空串,需要在过滤条件里加上len(word) > 0:
in_dict = {word:len(word) for word in D if len(word) > 0 and bool(re.search(pattern=".*".join(re.escape(c) for c in word), string=S))}
4. 大小写敏感问题
你的代码默认是大小写敏感的,如果S和D里的单词存在大小写差异(比如S="Abppplee",D={"able"}),会匹配失败。如果需要支持大小写不敏感,可以给re.search加上flags=re.IGNORECASE参数。
5. 大数据量下的效率问题
当S非常长(比如10^5字符)或者D包含大量单词时,正则匹配的效率会比较低。相比之下,双指针/迭代器的方法判断子序列会更高效:
def is_subsequence(word, s): it = iter(s) return all(char in it for char in word) def subseq(S, D): valid_words = [(word, len(word)) for word in D if is_subsequence(word, S)] if not valid_words: return "" # 或者根据需求返回其他默认值 return max(valid_words, key=lambda x: x[1])[0]
这个方法的时间复杂度是O(N*M),其中N是D的长度,M是单词的平均长度,在大多数场景下比正则更快,尤其是单词较长的时候。
总的来说,你的方法在简单场景下完全可行,但如果要覆盖所有边界情况,需要针对上面的点做相应调整。
内容的提问来源于stack exchange,提问作者opunsoars

