LC3 Longest Substring Without Repeating Characters 哈希/双指针

本文探讨了如何寻找字符串中最长的无重复字符子串,并提供了两种算法实现方案。第一种算法采用双指针和哈希集合的方法,复杂度为O(n²);第二种算法优化了左指针移动方式,提高了效率。

问题描述:

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given "abcabcbb", the answer is "abc", which the length is 3.

Given "bbbbb", the answer is "b", with the length of 1.

Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring"pwke" is a subsequenceand not a substring.


用一个哈希数据结构存储已经出现过的字符,考虑直接使用unordered_set(基于哈希的set)。很自然的想到Two Pointers,第一次实现的算法比较复杂,效率偏低,复杂度在O(n^2),提交后AC但是只超过了8%的submission。具体是一个大循环控制左边界,内部循环控制右边界,左边界每次递增,每次进入小循环首先清空set。代码:

int lengthOfLongestSubstring(string s) {
    unordered_set<int> bag;
    int i=0,j,maxL=0;
    for(i=0;i<s.length();i++){
        bag.clear();
        for(j=i;j<s.length();j++){
            if(bag.find(s[j])==bag.end())
                bag.insert(s[j]);
            else
                break;
        }
        maxL=max(maxL,j-i);
    }
    return maxL;
}

第二次参考网上的题解,考虑到大循环左边界每次都递增的方式是造成效率低的主要原因。在右边界向右延伸的过程中每次出现set中已有字符时,让左边界向右走一步并在set中删除左边界刚走过的那个字符,只要左边界删掉的不是右边界的这个字符就一直删(移动左边界)。代码:

int lengthOfLongestSubstring(string s) {
    unordered_set<int> bag;
    int i=0,j=0,maxL=0;
    while(j<s.length()){
        if(bag.find(s[j])==bag.end()){
            bag.insert(s[j++]);
            maxL=max(maxL,(int)bag.size());
        }
        else{
            bag.erase(s[i++]);
        }
    }
    return maxL;
}

使用哈希集合实现无重复字符的最长子串功能的 Java 代码通常采用滑动窗口的算法思想,以下结合代码详细分析其原理: ```java class Solution { public int lengthOfLongestSubstring(String s) { // 哈希集合,记录每个字符是否出现过 Set<Character> occ = new HashSet<Character>(); int n = s.length(); // 右指针,初始值为 -1,相当于在字符串的左边界的左侧,还没有开始移动 int rk = -1; int ans = 0; for (int i = 0; i < n; ++i) { if (i != 0) { // 左指针向右移动一格,移除一个字符 occ.remove(s.charAt(i - 1)); } while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) { // 不断地移动右指针 occ.add(s.charAt(rk + 1)); ++rk; } // 第 i 到 rk 个字符是一个极长的无重复字符子串 ans = Math.max(ans, rk - i + 1); } return ans; } } ``` ### 代码原理分析 1. **初始化**: - 创建一个 `HashSet` 类型的 `occ` 集合,用于存储当前滑动窗口内的字符,以此判断字符是否重复。 - 变量 `n` 记录字符串 `s` 的长度。 - 右指针 `rk` 初始化为 -1,表示还未开始移动。 - 变量 `ans` 用于记录最长无重复字符子串的长度,初始化为 0。 2. **左指针移动**: - 通过 `for` 循环遍历字符串,`i` 作为左指针。 - 当 `i` 不为 0 时,意味着左指针向右移动了一格,此时需要将左指针之前指向的字符从 `occ` 集合中移除,以保证集合中存储的是当前滑动窗口内的字符。 3. **右指针移动**: - 使用 `while` 循环不断移动右指针 `rk`,只要 `rk + 1` 不越界且 `occ` 集合中不包含 `s.charAt(rk + 1)`,就将该字符添加到 `occ` 集合中,并将 `rk` 右移一位。 4. **更新最长子串长度**: - 在每次移动右指针结束后,计算当前滑动窗口的长度 `rk - i + 1`,并与 `ans` 比较,取最大值更新 `ans`。 ### 复杂度分析 - **时间复杂度**:$O(n)$,其中 $n$ 是字符串的长度。左指针和右指针分别最多遍历一次字符串。 - **空间复杂度**:$O(|\Sigma|)$,其中 $\Sigma$ 表示字符集(即字符串中可以出现的字符),$|\Sigma|$ 表示字符集的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值