Skip to content

Commit

Permalink
feat: update solutions to lc problem: No.0003 (doocs#3893)
Browse files Browse the repository at this point in the history
No.0003.Longest Substring Without Repeating Characters
  • Loading branch information
yanglbme authored Dec 27, 2024
1 parent 7f10b62 commit c37cba6
Show file tree
Hide file tree
Showing 14 changed files with 309 additions and 383 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ tags:

<pre>
<strong>输入: </strong>s = "abcabcbb"
<strong>输出: </strong>3
<strong>输出: </strong>3
<strong>解释:</strong> 因为无重复字符的最长子串是 <code>"abc"</code>,所以其长度为 3。
</pre>

Expand Down Expand Up @@ -62,26 +62,15 @@ tags:

<!-- solution:start -->

### 方法一:双指针 + 哈希表

定义一个哈希表记录当前窗口内出现的字符,记 $i$ 和 $j$ 分别表示不重复子串的开始位置和结束位置,无重复字符子串的最大长度记为 `ans`
### 方法一:滑动窗口

遍历字符串 `s` 的每个字符 $s[j]$,我们记为 $c$。若 $s[i..j-1]$ 窗口内存在 $c$,则 $i$ 循环向右移动,更新哈希表,直至 $s[i..j-1]$ 窗口不存在 `c`,循环结束。将 `c` 加入哈希表中,此时 $s[i..j]$ 窗口内不含重复元素,更新 `ans` 的最大值
我们可以用两个指针 $l$ 和 $r$ 维护一个滑动窗口,使其始终满足窗口内没有重复字符,初始时 $l$ 和 $r$ 都指向字符串的第一个字符。用一个哈希表或者长度为 $128$ 的数组 $\textit{cnt}$ 来记录每个字符出现的次数,其中 $\textit{cnt}[c]$ 表示字符 $c$ 出现的次数

最后返回 `ans` 即可
接下来,我们依次移动右指针 $r$,每次移动时,将 $\textit{cnt}[s[r]]$ 的值加 $1$,然后判断当前窗口 $[l, r]$ 内 $\textit{cnt}[s[r]]$ 是否大于 $1$,如果大于 $1$,说明当前窗口内有重复字符,我们需要移动左指针 $l$,直到窗口内没有重复字符为止。然后,我们更新答案 $\textit{ans} = \max(\textit{ans}, r - l + 1)$

时间复杂度 $O(n)$,其中 $n$ 表示字符串 `s` 的长度
最终,我们返回答案 $\textit{ans}$ 即可

双指针算法模板:

```java
for (int i = 0, j = 0; i < n; ++i) {
while (j < i && check(j, i)) {
++j;
}
// 具体问题的逻辑
}
```
时间复杂度 $O(n)$,其中 $n$ 为字符串的长度。空间复杂度 $O(|\Sigma|)$,其中 $\Sigma$ 表示字符集,这里 $\Sigma$ 的大小为 $128$。

<!-- tabs:start -->

Expand All @@ -90,14 +79,14 @@ for (int i = 0, j = 0; i < n; ++i) {
```python
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
ss = set()
ans = i = 0
for j, c in enumerate(s):
while c in ss:
ss.remove(s[i])
i += 1
ss.add(c)
ans = max(ans, j - i + 1)
cnt = Counter()
ans = l = 0
for r, c in enumerate(s):
cnt[c] += 1
while cnt[c] > 1:
cnt[s[l]] -= 1
l += 1
ans = max(ans, r - l + 1)
return ans
```

Expand All @@ -106,15 +95,15 @@ class Solution:
```java
class Solution {
public int lengthOfLongestSubstring(String s) {
boolean[] ss = new boolean[128];
int ans = 0;
for (int i = 0, j = 0; j < s.length(); ++j) {
char c = s.charAt(j);
while (ss[c]) {
ss[s.charAt(i++)] = false;
int[] cnt = new int[128];
int ans = 0, n = s.length();
for (int l = 0, r = 0; r < n; ++r) {
char c = s.charAt(r);
++cnt[c];
while (cnt[c] > 1) {
--cnt[s.charAt(l++)];
}
ss[c] = true;
ans = Math.max(ans, j - i + 1);
ans = Math.max(ans, r - l + 1);
}
return ans;
}
Expand All @@ -127,14 +116,14 @@ class Solution {
class Solution {
public:
int lengthOfLongestSubstring(string s) {
bool ss[128]{};
int ans = 0;
for (int i = 0, j = 0; j < s.size(); ++j) {
while (ss[s[j]]) {
ss[s[i++]] = false;
int cnt[128]{};
int ans = 0, n = s.size();
for (int l = 0, r = 0; r < n; ++r) {
++cnt[s[r]];
while (cnt[s[r]] > 1) {
--cnt[s[l++]];
}
ss[s[j]] = true;
ans = max(ans, j - i + 1);
ans = max(ans, r - l + 1);
}
return ans;
}
Expand All @@ -145,14 +134,15 @@ public:
```go
func lengthOfLongestSubstring(s string) (ans int) {
ss := [128]bool{}
for i, j := 0, 0; j < len(s); j++ {
for ss[s[j]] {
ss[s[i]] = false
i++
cnt := [128]int{}
l := 0
for r, c := range s {
cnt[c]++
for cnt[c] > 1 {
cnt[s[l]]--
l++
}
ss[s[j]] = true
ans = max(ans, j-i+1)
ans = max(ans, r-l+1)
}
return
}
Expand All @@ -163,13 +153,15 @@ func lengthOfLongestSubstring(s string) (ans int) {
```ts
function lengthOfLongestSubstring(s: string): number {
let ans = 0;
const ss: Set<string> = new Set();
for (let i = 0, j = 0; j < s.length; ++j) {
while (ss.has(s[j])) {
ss.delete(s[i++]);
const cnt = new Map<string, number>();
const n = s.length;
for (let l = 0, r = 0; r < n; ++r) {
cnt.set(s[r], (cnt.get(s[r]) || 0) + 1);
while (cnt.get(s[r])! > 1) {
cnt.set(s[l], cnt.get(s[l])! - 1);
++l;
}
ss.add(s[j]);
ans = Math.max(ans, j - i + 1);
ans = Math.max(ans, r - l + 1);
}
return ans;
}
Expand All @@ -178,24 +170,22 @@ function lengthOfLongestSubstring(s: string): number {
#### Rust

```rust
use std::collections::HashSet;

impl Solution {
pub fn length_of_longest_substring(s: String) -> i32 {
let s = s.as_bytes();
let mut ss = HashSet::new();
let mut i = 0;
s.iter()
.map(|c| {
while ss.contains(&c) {
ss.remove(&s[i]);
i += 1;
}
ss.insert(c);
ss.len()
})
.max()
.unwrap_or(0) as i32
let mut cnt = [0; 128];
let mut ans = 0;
let mut l = 0;
let chars: Vec<char> = s.chars().collect();
let n = chars.len();
for (r, &c) in chars.iter().enumerate() {
cnt[c as usize] += 1;
while cnt[c as usize] > 1 {
cnt[chars[l] as usize] -= 1;
l += 1;
}
ans = ans.max((r - l + 1) as i32);
}
ans
}
}
```
Expand All @@ -209,13 +199,15 @@ impl Solution {
*/
var lengthOfLongestSubstring = function (s) {
let ans = 0;
const ss = new Set();
for (let i = 0, j = 0; j < s.length; ++j) {
while (ss.has(s[j])) {
ss.delete(s[i++]);
const n = s.length;
const cnt = new Map();
for (let l = 0, r = 0; r < n; ++r) {
cnt.set(s[r], (cnt.get(s[r]) || 0) + 1);
while (cnt.get(s[r]) > 1) {
cnt.set(s[l], cnt.get(s[l]) - 1);
++l;
}
ss.add(s[j]);
ans = Math.max(ans, j - i + 1);
ans = Math.max(ans, r - l + 1);
}
return ans;
};
Expand All @@ -226,14 +218,15 @@ var lengthOfLongestSubstring = function (s) {
```cs
public class Solution {
public int LengthOfLongestSubstring(string s) {
int n = s.Length;
int ans = 0;
var ss = new HashSet<char>();
for (int i = 0, j = 0; j < s.Length; ++j) {
while (ss.Contains(s[j])) {
ss.Remove(s[i++]);
var cnt = new int[128];
for (int l = 0, r = 0; r < n; ++r) {
++cnt[s[r]];
while (cnt[s[r]] > 1) {
--cnt[s[l++]];
}
ss.Add(s[j]);
ans = Math.Max(ans, j - i + 1);
ans = Math.Max(ans, r - l + 1);
}
return ans;
}
Expand All @@ -244,19 +237,18 @@ public class Solution {

```php
class Solution {
/**
* @param String $s
* @return Integer
*/
function lengthOfLongestSubstring($s) {
$n = strlen($s);
$ans = 0;
$ss = [];
for ($i = 0, $j = 0; $j < strlen($s); ++$j) {
while (in_array($s[$j], $ss)) {
unset($ss[array_search($s[$i++], $ss)]);
$cnt = array_fill(0, 128, 0);
$l = 0;
for ($r = 0; $r < $n; ++$r) {
$cnt[ord($s[$r])]++;
while ($cnt[ord($s[$r])] > 1) {
$cnt[ord($s[$l])]--;
$l++;
}
$ss[] = $s[$j];
$ans = max($ans, $j - $i + 1);
$ans = max($ans, $r - $l + 1);
}
return $ans;
}
Expand All @@ -268,62 +260,40 @@ class Solution {
```swift
class Solution {
func lengthOfLongestSubstring(_ s: String) -> Int {
var map = [Character: Int]()
var currentStartingIndex = 0
var i = 0
var maxLength = 0
for char in s {
if map[char] != nil {
if map[char]! >= currentStartingIndex {
maxLength = max(maxLength, i - currentStartingIndex)
currentStartingIndex = map[char]! + 1
}
let n = s.count
var ans = 0
var cnt = [Int](repeating: 0, count: 128)
var l = 0
let sArray = Array(s)
for r in 0..<n {
cnt[Int(sArray[r].asciiValue!)] += 1
while cnt[Int(sArray[r].asciiValue!)] > 1 {
cnt[Int(sArray[l].asciiValue!)] -= 1
l += 1
}
map[char] = i
i += 1
ans = max(ans, r - l + 1)
}
return max(maxLength, i - currentStartingIndex)
return ans
}
}
```

#### Nim

```nim
proc lengthOfLongestSubstring(s: string): int =
var
i = 0
j = 0
res = 0
literals: set[char] = {}
while i < s.len:
while s[i] in literals:
if s[j] in literals:
excl(literals, s[j])
j += 1
literals.incl(s[i]) # Uniform Function Call Syntax f(x) = x.f
res = max(res, i - j + 1)
i += 1
result = res # result has the default return value
```

#### Kotlin

```kotlin
class Solution {
fun lengthOfLongestSubstring(s: String): Int {
var char_set = BooleanArray(128)
var left = 0
val n = s.length
var ans = 0
s.forEachIndexed { right, c ->
while (char_set[c.code]) {
char_set[s[left].code] = false
left++
val cnt = IntArray(128)
var l = 0
for (r in 0 until n) {
cnt[s[r].toInt()]++
while (cnt[s[r].toInt()] > 1) {
cnt[s[l].toInt()]--
l++
}
char_set[c.code] = true
ans = Math.max(ans, right - left + 1)
ans = Math.max(ans, r - l + 1)
}
return ans
}
Expand Down
Loading

0 comments on commit c37cba6

Please sign in to comment.