双指针 滑动窗口

字符串 指针跳步思路

3. Longest Substring Without Repeating Characters

(1) Longest Substring Without Repeating Characters - LeetCode

思路:双指针动态维护

begin指针指向当前查找无重复的初始位置,cur指针向前遍历并完成查找。

        1:s[cur]在begin-cur范围内出现过,begin收缩至出现位置i的下一个(注意字符串问题经常有这种指针跳步情况需要处理),重新开始记录本轮查找路径,cur继续向前遍历。

        2:s[cur]在begin-cur范围内未出现过,cur继续向前遍历

        3:更新结果

具体实现的思考:

1.要判断s[cur]是否出现过,考虑使用字典思想

且s[cur]是字符类型,不能像整数类型一样使用数组直接进行哈希,考虑使用map作为查找表

使用map<string,int>,键为s[cur],出现过的元素存在map中

2.cur向前查找某个元素时需要与之前已经出现的字符串对比→查找map

map.find的平均time complexity为多少?是基于map的红黑树性质吗?

3.如果出现过,begin需要跳转到i+1

map值为位置下标i,方便查找时进行跳转,对s[cur]进行查找时若已出现,则表中记录的i正好为上一次s[cur]出现的位置下标

4.跳转后需要重新开始本轮记录

由begin跳到i收缩窗口时需要丢弃begin~i的查找表记录,故每次收缩窗口后需要更新map记录

实现代码一:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        //map记录本轮已经出现过的
        unordered_map<char,int> occured;
        int begin = 0;
        for(int cur=0;cur<s.size();cur++){
            char cur_str = s[cur];
            if(occured.find(cur_str) != occured.end()){//出现过
               //出现过 
                int i = occured[cur_str];//上一次出现的位置
                for(int j=begin;j<=i;j++){//清除begin~i
                    occured.erase(occured.find(s[j]));
                }
                begin = i + 1;//begin跳转
            }
            //未出现过
            occured.insert(pair(cur_str,cur));//更新map
            res = getmax(res,cur-begin+1);
        }
        return res;
    }
    int getmax(int a,int b){
        return a>b?a:b;
    }
};

哈希思想的常用操作是整数类型的数组实现,这里能否使用?

对字符串的查找另一个思想是转换为ASCII码的存储数字,这就转化成了整型

基于ASCII的特点,需要0~255范围的字符,故所需整型数组大小为256

数组下标为字符对应的数值,数组值为字符位置下标,这样查找单个字符的时间复杂度为O(1)

初始时未记录任何元素位置,数组值应该初始化为-1,故begin只能定义为窗口的上一个位置

若一个元素重复出现了,那么第一次出现时的位置一定在本次(第二次)的前面,但这个第一次出现的位置可能并不一定是本轮所判断的窗口范围(begin,cur】

只有在窗口范围的才能进行进行处理,窗口以外的将被丢弃

往前遍历时,本次元素s[cur]有三种情况:

1、未出现过

2、出现过且在本轮窗口内

3、出现过但并不在本轮窗口内(begin,cur】==未出现过

所以本轮未出现过的情况相当于有两种:1&3,等价于Amap[s[cur]] == -1 或 Amap[s[cur]] >begin

综上,本轮未出现过的情况是Amap[s[cur]] >begin

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        vector<int> Amap(256,-1);//记录字符串出现的位置实现哈希
        int begin = -1;//维护窗口的前一个元素
        for(int cur=0;cur<s.size();cur++){
            if(Amap[s[cur]] > begin){//在窗口范围内&出现过——此时哈希数组的值就是其上一次出现的位置
               begin = Amap[s[cur]];
            }
            Amap[s[cur]] = cur;
            res = getmax(res,cur-begin);
        }
        return res;
    }
    int getmax(int a,int b){
        return a>b?a:b;
    }
};

要记录无重复的最大子字符串长度,无重复即出现次数仅一次,一旦遇到出现次数>1次的字符则说明当前窗口存在重复字符,begin需要向前移动到这个重复字符后一个位置来收缩窗口,故

可从出现次数角度处理记录,找到出现次数>1的元素就说明找到了begin欲跳转的位置的下一位

实现代码三:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        unordered_map<char,int> occurtime;//值记录出现次数
        int begin = 0;
        for(int cur=0;cur<s.size();cur++){
            char cur_str = s[cur];
            occurtime[cur_str] ++;//更新出现次数
            while(occurtime[cur_str] > 1){//未更新到重复字符
                occurtime[s[begin]] --;
                begin++;   
            }
            res = getmax(res,cur-begin+1);
          }
        return res;
    }
    int getmax(int a,int b){
        return a>b?a:b;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值