leetcode. 3 (无重复字符的最长子串)

Q:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串 。

先用我自己的方法写了一下这道题:
主要是要考虑一些细节的地方:
先举个例子:shsiasyehdbxjxjsg
它可以分成以下几段:
sh
sia
ia
syehdbxj

xjsg
1.我本来想的是如果遇到前面出现过的字符,就在这断掉,然后重新开始新的字符,比如sia,syehdbxj,这样最长的子串长度是 syehdbxj.size()。但是我没有考虑如果前面加上 ia,也符合无重复子串的长度,这样最长子串长度就是iasyehdbxj.size()。这个是需要注意的一个地方。PS:这里要感谢晚上11.30还在陪我刷题的男朋友给我找出的问题。
2.如果遇到连续重复的字符,和遇到之前出现过的字符处理是不同的,所以我放在else if{} else{}里,分两段写了。
3.最后我把所有分出来的几段字符长度都放在一个vector数组里,本来还想写个从里面找最大值的函数。后来灵机一动直接用sort排序,然后最后一个数肯定就是最大值了。

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        if (s.empty())return 0;
        if (s.size() == 1)return 1;//只有一个字符就直接返回1
        char temp = s[0];
        string s1;
        vectorresult;//记录不同的子串的长度。其实也可以用两个计数器,一个用来计数,另一个用来存放之前的个数,然后两个比较,留下大的那个。不知道会不会比创建vector快一点。明天试一下。
        int count = 1, last;
        s1.push_back(s[0]);
        for (int i = 1; i

leetcode. 3 (无重复字符的最长子串)_第1张图片
尝试了上面代码里备注所说的计数问题,看看会不会对运行时间产生影响。加了一句:

count2=count>count2?count:count2;//count2 用来保存子串长度的最大值。

最后时间并没有什么改变,说明了主要的时间耗费,可能还是在for循环和各种比较上。

看一下官方给出的解决方法:
1.暴力法
列举出来所有的子字符串,然后通过一个bool函数检查每个子字符串中有没有重复的字符,如果没有,就返回true ,再找到其中最大的数即可。

编程中用到set容器: set容器和map容器类似,但是set容器只存储键,map容器是键-值对。比如存20个1,map里存的是<1,20>,set里存的是1。所以不管存多少个同样的数字,set里只有一个数。
因此,set容器适合用来查看某个数是否存在。set.find()返回的是某个数的位置。set.count返回的是某个数的是否存在,存在就return 1 。
代码如下:在986 / 987 这个例子超时了。代码先放在这,就是用的官方题解的Java版改的,看评论里也有好多人超时了。

class Solution {
public:
    bool allUnique(string s,int begin,int end)
    {
        setset1;
        for(int i=begin;i

2.滑动窗口:
下面是官方题解:

在暴力法中,我们会反复检查一个子字符串是否含有有重复的字符,但这是没有必要的。如果从索引 i 到 j - 1 之间的子字符串 Sij 已经被检查为没有重复字符。我们只需要检查 s[j]s[j] 对应的字符是否已经存在于子字符串 Sij 中。滑动窗口是数组/字符串问题中常用的抽象概念。
窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即 [i, j)(左闭,右开)。而滑动窗口是可以将两个边界向某一方向“滑动”的窗口。例如,我们将 [i, j)向右滑动 1 个元素,则它将变为 [i+1, j+1)(左闭,右开)。
回到我们的问题,我们使用 HashSet 将字符存储在当前窗口 [i, j)[i,j)(最初 j = i)中。 然后我们向右侧滑动索引 j,如果它不在 HashSet 中,我们会继续滑动 j。直到 s[j] 已经存在于 HashSet 中。此时,我们找到的没有重复字符的最长子字符串将会以索引 i 开头。如果我们对所有的 i 这样做,就可以得到答案。

在实际编程中,发现一个小问题,就是如果用官方题解提供的set模板,向s1中插入字符,会自动按照字符的ASCII码进行排序,这样就打乱了插入的顺序:如测试序列 “ynyo”: n的ASCII码是110 ,y的ASCII码是121,本来想插入的顺序是y,n ,但是insert之后就按照ASCII码大小顺序排序,s1中存储就变成了 n,y。具体的解决办法没想到,就换用了string类型。

class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        int i=0,j=0,last=0;
        string s1;
        //sets1;
        while(i

其实感觉这个思想和我之前自己写的那个方法的思想类似,运行时间也是一样的。

3.优化滑动窗口
原来的方法中,i 是一个一个的蹦过去,现在就直接让 i 跳到出现重复字符的位置即可。

当我们知道该字符集比较小的时侯,我们可以用一个整数数组作为直接访问表来替换 Map。
常用的表如下所示:

int [26] 用于字母 ‘a’ - ‘z’或 ‘A’ - ‘Z’
int [128] 用于ASCII码
int [256] 用于扩展ASCII码

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        vectorv(128,0);
        int t=0;int ans=0;
        for(int i=0;i

leetcode. 3 (无重复字符的最长子串)_第2张图片
运行并不是很稳定,有时候12ms,有时候20ms。我也不知道为啥

你可能感兴趣的:(leetcode. 3 (无重复字符的最长子串))