最长回文子串哈希二分
时间: 2024-02-01 12:05:15 浏览: 103
最长回文子串哈希二分是一种求解最长回文子串的方法。首先,我们可以使用字符串哈希算法计算出给定字符串的正向哈希和反向哈希。然后,我们通过枚举间断点来讨论两种情况:回文序列为奇数和回文序列为偶数。
当回文序列为奇数时,我们将枚举点设置为中间点,然后利用二分法在回文串的一半长度上进行二分。这意味着我们每次将回文串的长度缩小一半,直到找到最长的回文子串。
当回文序列为偶数时,我们将枚举点设置为中间间隔。同样,我们使用二分法在回文串的一半长度上进行二分。每次我们将回文串的长度缩小一半,直到找到最长的回文子串。
这种方法利用了哈希算法的特性,通过二分法的思想来缩小搜索范围,从而更快地找到最长的回文子串。
相关问题
Rabin-Karp 二分 最长回文子串
### 使用 Rabin-Karp 算法结合二分法查找最长回文子串
为了找到给定字符串中的最长回文子串,可以采用一种组合策略:利用二分查找来决定可能的最大长度,并通过哈希函数验证该长度下的子串是否为回文。这种方法能够有效地减少不必要的比较次数。
#### 基本思路
1. 定义一个辅助函数 `is_palindrome` 来判断指定位置和长度的子串是不是回文;
2. 对于每一个潜在的中心点(单字符或双字符),尝试扩展到最大范围内的回文;
3. 利用二分查找技术,在已知最小值0和当前发现的最大回文字串长度之间进行搜索;
4. 在每次迭代过程中应用Rabin-Karp算法快速检测是否存在相同长度的不同起始位置但具有相等哈希值的子串;如果找到了,则进一步确认这些候选者确实是回文并更新最优解。
下面是一个Python实现的例子:
```python
def rabin_karp_hash(s, p=1_000_000_007, a=256):
"""计算字符串s基于质数p以及基数a的滚动散列"""
hash_value = 0
for char in s:
hash_value = (hash_value * a + ord(char)) % p
return hash_value
def check_palindrome(text, length):
"""检查text中是否有length长度的回文子串."""
if not text or len(text) < length:
return False
MOD = 1_000_000_007
BASE = 256
powerL = pow(BASE, length-1, MOD)
hashes = set()
current_hash = rabin_karp_hash(text[:length], MOD, BASE)
for i in range(len(text)-length+1):
if str(text[i:i+length]) == str(text[i:i+length])[::-1]:
return True
next_char_index = i + length
if next_char_index < len(text):
current_hash = ((current_hash - ord(text[i]) * powerL) * BASE +
ord(text[next_char_index])) % MOD
while current_hash < 0:
current_hash += MOD
if current_hash in hashes and \
str(text[i+1:i+length+1]) == str(text[i+1:i+length+1])[::-1]:
return True
else:
hashes.add(current_hash)
return False
def longest_palindromic_substring_with_rk_and_binary_search(s):
lo, hi = 0, len(s)+1
best_len = 0
result = ""
while lo <= hi:
mid = (lo + hi)//2
found = check_palindrome(s, mid)
if found:
best_len = max(best_len, mid)
result = get_any_palindrome_of_length(s, mid)
lo = mid + 1
else:
hi = mid - 1
return result
def get_any_palindrome_of_length(s, l):
n = len(s)
for start in range(n-l+1):
substr = s[start:start+l]
if substr == substr[::-1]:
return substr
raise ValueError(f"No palindrome of length {l} exists.")
```
此代码实现了上述提到的功能,其中包含了几个重要的部分:
- 计算字符串哈希值的方法 `rabin_karp_hash()`,
- 验证特定长度下是否存在回文的方法 `check_palindrome()`,
- 结合二分查找逻辑寻找最长达标的回文子串的核心过程 `longest_palindromic_substring_with_rk_and_binary_search()`.
最长回文子串nlogn
### Manacher算法之外的选择
对于给定的问题,虽然Manacher算法能够在O(n)的时间复杂度下找到最长回文子串[^1],但存在其他方法可以在稍高的时间复杂度\(O(n\log n)\)内完成同样的任务。这些替代方案通常基于二分查找与哈希或后缀数组等数据结构相结合的方式。
#### 使用二分查找加哈希的方法
这种方法的核心在于通过二分查找来猜测可能的最大半径r,并验证是否存在这样的半径使得以任意字符为中心的子串是回文的。为了高效地检验这一点,可以预先计算字符串及其反转后的滚动哈希值。这样做的好处是可以快速比较两个不同位置处相同长度的子串是否相等而无需逐字对比。
```cpp
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int MOD = 1e9+7; // 哈希模数
const int BASE = 26; // 字符集大小(假设只有小写字母)
// 计算前缀和后缀hash表
void calc_hashes(const string& s, vector<ll>& prefix_hash, vector<ll>& power){
int n = s.size();
prefix_hash.resize(n);
power.resize(n);
power[0]=1;
for(int i=1;i<n;++i)
power[i]=(power[i-1]*BASE)%MOD;
prefix_hash[0]=s[0]-'a'+1;
for(int i=1;i<n;++i)
prefix_hash[i]=(prefix_hash[i-1]*BASE+(s[i]-'a'+1))%MOD;
}
ll get_substring_hash(int L,int R,vector<ll>& hash_table,vector<ll>& pow_table){
if(L==0)return hash_table[R];
return ((hash_table[R]-pow_table[R-L+1]*hash_table[L-1])%MOD+MOD)%MOD;
}
bool check_palindrome(string &str,ll mid){
auto rev_str=str;
reverse(rev_str.begin(),rev_str.end());
vector<ll> orig_hash(str.length()),orig_pow(str.length());
vector<ll> rev_hash(rev_str.length()),rev_pow(rev_str.length());
calc_hashes(str,orig_hash,orig_pow);
calc_hashes(rev_str,rev_hash,rev_pow);
for(size_t center=0;center<str.length();++center){
size_t l=center-mid,r=center+mid;
if(l>=0 && r<str.length()){
if(get_substring_hash(l,r,orig_hash,orig_pow)==get_substring_hash(r,l,rev_hash,rev_pow))
return true;
}
}
return false;
}
int longest_palindromic_substring_nlogn(const string& str){
int low=0,high=(int)(str.length()/2)+1,res=-1;
while(low<=high){
int mid=(low+high)/2;
if(check_palindrome((string&)str,mid)){
res=max(res,(mid<<1)|!(res==-1));
low=mid+1;
}else{
high=mid-1;
}
}
return max(res,0);
}
```
此代码片段展示了如何利用二分查找配合哈希技术在一个接近线性的额外空间消耗上达到O(n log n)的整体性能表现。需要注意的是实际应用中应当考虑更多细节优化以及处理边界条件等问题[^4]。
阅读全文
相关推荐















