Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

更新 0028.实现strStr.md,增加字符串哈希的解法,时间复杂度为O(n) #2544

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 49 additions & 0 deletions problems/0028.实现strStr.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,54 @@ public:
* 空间复杂度: O(m)


## 思路2
本道题可以使用字符串哈希来做,时间$O(n)$的时间复杂度
我们把字符串看成是一个P进制数(经验来说,取131,或者13331),这样的哈希冲突概率很低
然后预处理出$1-i(i \in [1,n])$这个的哈希值,存在$h[]$数组里,这样我们就可以$O(1)$的时间复杂度来求出任意子串的哈希值
任意子串的哈希值 = $ h[r] - h[l-1] * P^(r-l+1) $

在本题中:
1. 我们先求出第一个haystack的1-i的每个字符串的哈希
2. 然后求出needle的哈希 这是一个定值了。
3. 然后只需要遍历haystack以i为起点,长度为的子串,求出哈希值,和needle的哈希值比较即可

> 1. 哈希值的计算涉及大量的乘法和加法操作。当处理长字符串时,这些中间计算结果可能会超出 `int` 或 `long long` 的表示范围,导致整数溢出。
>
> 2. 使用 `ull` 类型可以确保哈希值的计算不会发生溢出,因为 `ull` 是 64 位无符号整数,其表示范围为 0 到 18,446,744,073,709,551,615,足以容纳绝大多数字符串的哈希值。

时间复杂度:$O(n)$

空间复杂度:$O(n)$


具体代码如下
```cpp
class Solution {
public:
typedef unsigned long long ull; // 定义 64 位无符号整数类型 ull
int P = 13331; // 定义哈希函数的基数为 13331
ull p[10010], h[10010]; // 定义用于存储指数幂和哈希值的数组

int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size(); // 获取两个字符串的长度
haystack = " " + haystack, needle = " " + needle; // 在字符串前添加一个空格,使下标从 1 开始 方便计算
p[0] = 1,h[0]=0; // 初始化指数幂数组的第一个元素为 1
for (int i = 1; i <= n; i++) {
p[i] = p[i - 1] * P; // 用来计算 P 的 i 次方,即 P^i
h[i] = h[i - 1] * P + haystack[i]; // h[i] 表示前 i 个字符的哈希值
}
ull t = 0;
for (int i = 1; i <= m; i++) t = t * P + needle[i]; // 计算 needle 字符串的哈希值
for (int i = 1; i + m - 1 <= n; i++) {
ull v = h[i + m - 1] - h[i - 1] * p[m]; // 计算当前子串的哈希值
if (v == t) return i - 1; // 如果哈希值相等,返回当前子串的起始下标
}
return -1; // 如果没有找到匹配项,返回 -1
}
};

```

## 总结

我们介绍了什么是KMP,KMP可以解决什么问题,然后分析KMP算法里的next数组,知道了next数组就是前缀表,再分析为什么要是前缀表而不是什么其他表。
Expand Down Expand Up @@ -1460,3 +1508,4 @@ public int[] GetNext(string needle)
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a>