zoukankan      html  css  js  c++  java
  • 【Leetcode】无重复字符的最长子串

    暴力解法,枚举所有子字符串组合

    输入:长度[0,n]的字符串

    耗时过长---

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int len = s.size();
            int ans = 1;
            
            if(s.empty()){
                return 0;
            }
            
            for(int i = 0; i < len-1; i++){
                for(int j = i+1; j < len; j++){
                    if(allUnique(s, i, j)){
                        ans = max(ans, j-i+1);
                    }
                }
            }
            return ans;
        }
        
        bool allUnique(string ss, int start, int end){
            std::set<char> ss_set;
            for(int i = start; i <= end; i++){
                char ch = ss[i];
                if(ss_set.count(ch)){
                    return false;
                }
                ss_set.insert(ss[i]);
                
            }
            return true;
        }
    };

    第二种方法:改进暴力解法

    子字符串为[i,j),左闭右开。一旦检测到新加入字符已存在于已有子字符串中,则返回当前子字符串长度,清空子字符串,左边界右移(i+1),重新生成新的子字符串。

    依然耗时过长

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int len = s.size();
            std::cout << "len is : " << len << std::endl;
            int ans = 1;
            std::set<char> ss_set;
            int begin = 0;
            
            if(s.empty()){
                return 0;
            }
            else{
                
                while(begin < len-1) {
                    //std::cout << "begin is : " << begin << std::endl;
                    int tmp = 0;
                    ss_set.clear();
                    for(int j = begin; j < len; j++){
                        if(ss_set.count(s[j])){
                            break;
                        }
                        ss_set.insert(s[j]); 
                        tmp++;
                    }
                    begin = begin + 1;
                    ans = max(ans, tmp);
                }
                
            }
            return ans;
        }
    
    };

    第三种:滑窗法

    改进第二种方法。子字符串为[i,j),左闭右开。一旦检测到新加入字符已存在于已有子字符串中,则返回当前子字符串长度,删除begin位置的字符,窗口右移(i+1,count-1),新加入字符与窗口中的元素进行对比。

    耗时94ms,消耗内存21.3MB

    TIP

    1. 通过使用 HashSet 作为滑动窗口,我们可以用 O(1) 的时间来完成对字符是否在当前的子字符串中的检查;

    2. 滑动窗口是数组/字符串问题中常用的抽象概念。 窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即 [ij)(左闭,右开)。而滑动窗口是可以将两个边界向某一方向“滑动”的窗口。例如,我们将 [ij) 向右滑动 11 个元素,则它将变为 [i+1,j+1)(左闭,右开)。

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int len = s.size();
            std::set<char> ss_set;
            int ans = 1, begin = 0, count = 0, index = 0;
            set<char>::const_iterator iter;
            
            if(s.empty()){
                return 0;
            }
            else{
                
                while(begin < len-1) {
                    
                    for(int j = index; j < len; j++){
                        if(ss_set.count(s[j])){
                            index = begin + count;
                            break;
                        }
                        ss_set.insert(s[j]); 
                        count++;
                    }
                    
                   // for(iter = ss_set.begin(); iter != ss_set.end(); iter++){
                   //     std::cout << *iter << " " ;
                   // }
                   // std::cout << std::endl;
                    
                    ss_set.erase(s[begin]);
                    begin = begin + 1;
                    ans = max(ans, count);
                    count = count - 1;
                }
            }
            return ans;
        }
    
    };

    滑窗法比较清晰的表达,引入变量i,j表示窗口的范围。

    注意 i++ 和 ++i 的区别

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int len = s.size();
            std::set<char> ss_set;
            int ans = 1, i = 0, j = 0;if(s.empty()){
                return 0;
            }
            else{
                while(i < len && j < len){
                    if(!ss_set.count(s[j])){
                        ss_set.insert(s[j++]);
                        ans = max(ans, j-i);
                    }
                    else{
                        ss_set.erase(s[i++]);
                    }
                }
            }
            return ans;
        }
    
    };

    优化滑窗法

    我们可以定义字符到索引的映射,而不是使用集合来判断一个字符是否存在。 当我们找到重复的字符时,我们可以立即跳过该窗口。

    也就是说,如果 s[j] 在 [i,j) 范围内有与 j′ 重复的字符,我们不需要逐渐增加 i 。 我们可以直接跳过 [ij] 范围内的所有元素,并将 ii 变为 j' + 1j+1。

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int len = s.size();
            int ans = 0, i = 0, j = 0;
            map<char, int> str_map;
            
            for(j; j < len; j++){
                if(str_map.count(s[j])){
                    i = max(i, str_map.find(s[j])->second);
                }
                
                ans = max(ans, j - i + 1);
                str_map[s[j]] = j+1;  //窗口右边界右移
                
            }
            return ans;
        }
    
    };
  • 相关阅读:
    第五周总结
    第四周总结
    第三周总结
    开课博客
    学习进度
    个人作业1-数组
    数组
    第一周考试总结
    团队个人冲刺第六天
    团队个人冲刺第五天
  • 原文地址:https://www.cnblogs.com/gdut-gordon/p/10590819.html
Copyright © 2011-2022 走看看