[英]Check if string contains pattern (with respect to one-to-one symbols mapping)
我正在尝试解决以下问题:
检查,如果字符串包含子字符串,可以使用一对一小写符号替换(使用原始小写字母和新字母之间的双射)从模式中获得。
我的字符串只包含大写和小写英文字母 ( A–Z, a–z ) 。
例如:
正面:
字符串: justanexampleXstring
模式: onecompleX
答案: true (anexampleX <-> onecompleX,使用映射:a<->o, x<->c;大写不受映射影响)
消极的:
字符串: AaBbDd
图案: AaBa
答案: false (模式中有两个重复的小写字母,原始字符串中没有重复的小写字母)
目前我正在考虑在多模式搜索设置中使用Rabin-Karp算法:
搜索 k 个模式的一种简单方法是重复单模式搜索,花费 O(n+m) 时间,总计 O((n+m)k) 时间。 相比之下,上面的算法可以在 O(n+km) 预期时间内找到所有 k 个模式,假设哈希表检查在 O(1) 预期时间内工作。
但是对所有可能映射生成的字符串进行简单检查会导致 k= 26! (阶乘,26个字母字母表之间所有可能的一对一映射的数量)
对当前方法的任何想法或优化将不胜感激。 先感谢您!
您可以在相同的预期运行时间内对 Rabin-Karp 稍作更改(假设您的散列函数满足通常的一致性要求)。 这个想法是考虑什么样的字符串相当于小写字母双射映射下的模式。 首先,所有大写字母必须完全匹配,所以我们暂时可以忽略它们。 对于小写字母,重要的是基于相等字符的索引分区。
例如,如果 pattern = 'banana',其长度为m=6
,那么索引的分组将是 3 组,一组用于 'b' 索引,一组用于 'a's,一组用于 'n's。
(无序)分区则是([0], [1, 3, 5], [2, 4])
。 现在,让我们找到一个等效的字符串表示。 而不是这个分区,用到下一个相等字符(即,其分区组中的下一个较大值)或m
的距离替换模式的每个小写字符,如果这是字符的最终出现。 这意味着我们替换的 'banana' 字符串变为
(6, 2, 2, 2, 6, 6)
; 另一个长度为 m 的小写字符串具有相同的“替换”字符串,当且仅当它匹配等值索引分组下的模式,当且仅当它们在字母双射下匹配时才匹配模式。
我们可以使用长度为m
Rabin-Karp 滚动散列,替换上面的小写字母,并将大写字母映射到 'm' 之上的另一个数字范围(例如,m+1 到 m+26)。 唯一的问题是当我们的窗口向右滑动时,窗口中间的字符可能会改变值:
给定我们替换的 'banana' (6, 2, 2, 2, 6, 6)
字符串,如果我们添加到字符串末尾的下一个字符是 'a',我们替换的 'ananaa' 字符串是(2, 2, 2, 6, 1, 6)
。 我们可以通过在处理字符串时为每个小写字母实现“上次看到”索引的哈希图来解决这个问题。 使用滚动散列的素数幂系数之和,我们可以快速计算出所需的变化。
如果你想知道更新滚动散列的确切数学:在从 Rabin-Karp 执行通常的窗口移位更新之后,如果添加的字符是小写的并且在我们的窗口中更早出现j
字符,它的字符值来自 'm'到'j'。 对于素数p
,它对“滚动散列模和”的贡献从m*(p^j)
变为j*(p^j)
,我们可以减去差异。 任何可以轻松计算任何窗口字符对哈希的当前贡献的滚动哈希也将起作用。
一个更有趣的问题是 Knuth-Morris-Pratt 或 Boyer-Moore 是否可以适应以类似的方式工作? 我还没有找到类似的转换,因为我的算法取决于滚动哈希。 尽管如此,Rabin-Karp 的平均和最坏情况运行时间将保持不变。
我不知道 Rabin-Karp,但这比 k=26 好! 你提到过。 我将像onecompleX
这样的模式变成像(.)(.)(.)(.)\\1(.)(.)(.)\\3X
这样的正则表达式:
import re
def solve(string, pattern):
def replace(match, group={}):
letter = match[0]
if letter not in group:
group[letter] = len(group) + 1
return '(.)'
return f'\\{group[letter]}'
regex = re.sub('[a-z]', replace, pattern)
return bool(re.search(regex, string))
print(solve('justanexampleXstring', 'onecompleX'))
print(solve('AaBbDd', 'AaBa'))
输出( 在线尝试! ):
True
False
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.