字符串hash + 二分答案 - 求最长公共子串 --- poj 2774

博客介绍了如何利用字符串hash和二分搜索来解决求两个字符串最长公共子串长度的问题。通过设定较短字符串长度为初始高界,然后进行二分查找,结合特定的判断函数实现效率为O(n)的解决方案。源代码包含详细注释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 Long Long Message

Problem's Link:http://poj.org/problem?id=2774


 

Mean: 

 求两个字符串的最长公共子串的长度。

analyse:

 前面在学习后缀数组的时候已经做过一遍了,但是现在主攻字符串hash,再用字符串hash写一遍。

这题的思路是这样的:

1)取较短的串的长度作为high,然后二分答案(每次判断长度为mid=(low+high)>>1是否存在,如果存在就增加下界;不存在就缩小上界);

### 使用字符串哈希算法最长公共子串 #### 字符串哈希基础概念 字符串哈希是一种将字符串转换为整数值的技术,使得可以通过比较这些值来间接比较两个字符串是否相同。这种方法能够显著提高某些操作的速度,尤其是在处理大量字符串匹配问题时。 对于给定的两个字符串`str1`和`str2`,目标是在这两个字符串之间找到最长公共子串。为了利用字符串哈希解决这个问题,通常采用二分法配合滚动哈希技术来进行高效查询[^1]。 #### 实现思路 核心思想在于假设一个可能的最大长度`len`,并验证是否存在这样的长度满足条件——即在这个长度下至少有一组相等的子串存在于两输入字符串之中。具体过程如下: - **初始化**:设定最小猜测长度`low=0`以及最高可能性`high=min(strlen(str1), strlen(str2))`。 - **循环判断**:当`low<=high`时执行以下逻辑: - 计算中间值`mid=(low+high)/2`; - 对于每一个起始位置,在两个字符串上计算长度为`mid`的所有子串对应的哈希值; - 如果发现有相同的哈希值,则说明找到了长度不小于`mid`的有效答案,更新当前最佳记录,并设置新的搜索范围为更大的一半(`low=mid+1`);反之则缩小至更短的一半(`high=mid-1`); 最终得到的结果将是最后一次成功匹配到的长度及其对应的具体子串内容。 #### Python代码示例 下面给出一段基于上述原理编写的Python程序片段用于演示如何应用此方法解决问题: ```python def get_hash(s, p, m): """获取base-p模m下的字符串s的hash""" hash_value = 0 for c in s: hash_value = (hash_value * p + ord(c)) % m return hash_value def rabin_karp_search(text, pattern, base, mod): n, m = len(text), len(pattern) # Compute the hash value of text and pattern h_text = get_hash(text[:m], base, mod) h_pattern = get_hash(pattern, base, mod) power_of_base = pow(base, max(m-1, 0), mod) found_positions = [] if h_text == h_pattern: found_positions.append(0) for i in range(1, n-m+1): h_text = ((h_text - ord(text[i-1])*power_of_base)*base + ord(text[i+m-1]))%mod if h_text < 0: h_text += mod if h_text == h_pattern: found_positions.append(i) return found_positions def longest_common_substring_with_binary_search(s1, s2, base=257, mod=int(1e9)+7): low, high = 0, min(len(s1), len(s2)) best_len = 0 result = "" while low <= high: mid = (low + high)//2 seen_hashes_s1 = set() hashes_s1 = {} for start_pos in range(len(s1)-mid+1): substring = s1[start_pos:start_pos+mid] current_hash = get_hash(substring, base, mod) if current_hash not in hashes_s1: hashes_s1[current_hash] = [] hashes_s1[current_hash].append(start_pos) matches_found = False for start_pos in range(len(s2)-mid+1): substring = s2[start_pos:start_pos+mid] current_hash = get_hash(substring, base, mod) if current_hash in hashes_s1: positions_in_s1 = hashes_s1[current_hash] for pos in positions_in_s1: if s1[pos:pos+len(substring)] == substring: matches_found = True break if matches_found: break if matches_found: best_len = mid result = substring low = mid + 1 else: high = mid - 1 return {"length":best_len,"substring":result} ``` 该函数接受两个参数`s1`和`s2`作为要对比的目标字符串,并返回它们之间的最长公共子串的信息。这里采用了Rabin-Karp算法中的多项式滚动散列机制来子串哈希运算效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值