3. Longest Substring Without Repeating Characters (无重复字符的最长子串)滑动窗口

问题

英文

3. Longest Substring Without Repeating Characters
(无重复字符的最长子串)

在这里插入图片描述
在这里插入图片描述

中文

在这里插入图片描述

代码

小白的码

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

class SolutionAC {
public:
    int lengthOfLongestSubstring(string s)
    {
        if (s == "")
            return 0;
        char sSubC[50010];
        int arr[130]; // 统计滑动窗口中字符出现是否超过2
        int memsize = sizeof(int) * 130;
        memset(arr, 0, memsize);
        int ssize = s.size();
        int index = 0;
        int len = 1;
        int lentmp;
        // int flag = 0; // 作为标记,用来标记是否需要减去多加的。(当前值已经开始重复了,但是被计算在 lentmp中,进而混进了len中
        int i;
        for (i = 0; i < ssize; i++)
        {
            arr[s[i]]++;
            if (arr[s[i]] > 1)
            {
                lentmp = i - index; // 每次到临界点时,统计一下最大lentmp,再和len比较
                if (lentmp > len) len = lentmp;
                while (arr[s[i]] > 1) // 调整窗口,直到不含重复字符
                {
                    index++;
                    arr[s[index - 1]]--;
                }
            }
        }
        lentmp = i - index; // 最后一次统计的lentmp(针对abcdeg.....)
        if (lentmp > len) len = lentmp;
        return len;
    }
};

class SolutionTimeLimitExceeded // 遍历,超时
{
public:
    int lengthOfLongestSubstring(string s)
    {
        if (s == "")
            return 0;
        int arr[130];
        int len = 1;
        // int subSize;
        int a;
        int flag;
        char sSubC[50010]; // 用个字符数组来存子串
        int ssize = s.size();
        int memsize = sizeof(int) * 130;
        // int eor = 0;
        for (int i = 0; i < ssize; i++)
        {
            for (int j = len + 1; j <= ssize - i; j++)
            {
                // cout << s.substr(i, j) << endl;
                memset(arr, 0, memsize);
                strcpy(sSubC, s.substr(i, j).c_str()); // c_str

                flag = 1; // 没有重复字符
                // subSize = sSubC.size();
                for (int k = 0; k < j; k++)
                {
                    a = sSubC[k];

                    arr[a]++;
                    if (arr[a] > 1)
                    {
                        flag = 0;
                        // cout <<"a = " <<  a << " " << arr[a] << endl;
                        break;
                    }
                }
                if (flag == 1)
                {
                    len = j;
                }
            }
        }
        return len;
    }
};

int main()
{
    string str;
    Solution sol;

    cin >> str;
    cout << sol.lengthOfLongestSubstring(str) << endl;
    return 0;
}

大佬的码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 哈希集合,记录每个字符是否出现过
        unordered_set<char> occ; // occurence 出现
        int n = s.size();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int righPtr = -1, ans = 0;
        // 枚举右指针的位置,初始值隐性地表示为 -1
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 指针向右移动一格,移除一个字符(尝试移除重复的字符,直到删除为止,不然进不去下面的 while循环)
                occ.erase(s[i - 1]);
            }
            while (righPtr + 1 < n && !occ.count(s[righPtr + 1])) { // 当哈希集合没有当点指针所指的字符时
                // 不断地移动右指针
                occ.insert(s[righPtr + 1]);
                ++righPtr;
            }
            // 第 i 到 righPtr 个字符是一个无重复字符的子串
            ans = max(ans, righPtr + 1 -i);
        }
        return ans;
    }
};

知识点

unordered_set 容器具有以下几个特性:

不再以键值对的形式存储数据,而是直接存储数据的值;
容器内部存储的各个元素的值都互不相等,且不能被修改。
不会对内部存储的数据进行排序
(这和该容器底层采用哈希表结构存储数据有关,

template < class Key,  
    class Hash = hash<Key>,  
    class Pred = equal_to<Key>,  
    class Alloc = allocator<Key> > class unordered_set;  
C++ 11中对unordered_set描述大体如下:
无序集合容器(unordered_set)是一个存储唯一
(unique,即无重复)的关联容器(Associative container),
容器中的元素无特别的秩序关系,该容器允许基于值的快速元素检索,
同时也支持正向迭代。
在一个unordered_set内部,元素不会按任何顺序排序,
而是通过元素值的hash值将元素分组放置到各个槽(Bucker,也可以译为“桶”),
这样就能通过元素值快速访问各个对应的元素(均摊耗时为O(1))。
原型中的Key代表要存储的类型,
而hash<Key>也就是你的hash函数,
equal_to<Key>用来判断两个元素是否相等,
allocator<Key>是内存的分配策略。

我们就可以使用「滑动窗口」来解决这个问题:
我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,
其中左指针代表着「枚举子串的起始位置」,而右指针即为用来向右移动到下一项

在每一步的操作中,当我们将左指针向右移动一格,表示
我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,
但需要保证这两个指针对应的子串中没有重复的字符。
在移动结束后,这个子串就对应着
以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;
在枚举结束后,我们找到的最长的子串的长度即为答案。

总结

多思考,多动手,多学习!感受生活的充实!
在这里插入图片描述
我是用的滑动窗口的思路,代码实现是用 一个含130个整型值的数组对应字符的ASCII 码,来统计字串中是否出现重复的字符,更简单一些!!!
思路大体一致,很明显,自己还有很多很多提升改进的地方!!路漫漫!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值