leetcode 3

首先分析一下时间最短的解法
int lengthOfLongestSubstring(string s)
{
     vector<int> vec(256,-1);
     int i,start=-1,maxLength=0; 

     for(i=0;i<s.size();i++)
    {
         if(vec[s[i]]>start)
             start=vec[s[i]];
         vec[s[i]]=i;
        maxLength=max(maxLength,i-start);
      }
      return maxLength;
}


以s="abcabcab"为例,输出start \t maxLength \n结果如下:

-1      1
-1      2
-1      3
0       3
1       3
2       3
3       3
4       3
最终结果maxLength为3;

     然后再考虑一下极端情况:

1.如果输入空串:不进入循环,直接return 0,正确;

2.如果输入只有一个元素:进行一次循环,maxLength=0-(-1)=1,也正确;

3.如果输入多个同样的元素:由于start总能及时更新到最近一次重复的位置,因此结果也是正确的;

4.这种情况描述起来有点复杂,比如这样一个字符串,“abcade" 我之前写过的代码就栽在这一处了,这一字符串符合题意的子串应该是”bcade"而不是“abc"或”ade", 那么我们看一下这段代码能不能处理好这个问题:同样运行一下,输出每个start  '\t' maxLength:

-1      1
-1      2
-1      3
0       3
0       4
0       5

很显然是通过验证了的。

        顺便分析一下为什么我的代码会出现这个问题。

int lengthOfLongestSubstring(string s) {
        
        int i,size=0,re=0;
        unordered_map<char,int> tmp;
        for(i=0;i<s.size();)
        {
            if(tmp.find(s[i])==tmp.end())
            {
                size++;
                tmp[s[i]]=i;
                i++;
            }
            else 
            {
                re=size>re?size:re;
                size=0;
                tmp.clear();
            }
        }
        re=size>re?size:re;
        return re;
    }

     这是我原来写的代码。

    仔细分析,sample代码保证了最初是从s[0]开始计数,之后如果再次遍历到之前已经出现过的元素,就从上一次该元素出现位置的下一个开始计数。而反观自己的思路,很

重要的一点是,我的代码如果遍历到了之前出现过的元素,就从当前位置开始计数,而且之前保存过的数据全部丢失,而且辣么长,是很糟糕的代码了,\哭泣。

从这例子中可以看出,start在这里的作用是刷新开头,以保证substring里没有重复元素;maxLength用来保存最大长度,它取决于之前有过的最大长度和现在的长度哪个更大;而vec的存在保证及时更新重复元素的位置,也就是如果一个元素出现过两次以上,vec总能记录最后一个位置。其实回头来想想,要找一个substring的长度,只需要知道起始和结尾两个index,这里start和i就是承担这个责任的;更进一步,如果找到没有重复元素的substring长度,就需要做到遍历到第一个之前出现过的元素时,及时转变为下一个substring的开头,这里start的作用设计的很巧妙。当然要找到最大不重复substring的长度,还要有一个记录最大长度的值,那就是maxLength了。到现在我们已经可以完全理解这段代码是怎样起作用的了,接下来我们要理解为什么有人能想到这么设计。

首先我们宏观的看待这个问题,怎样找到最大的不重复substring长度?要找到“最大”,肯定要记录并比较;要找到“substring”,肯定要遍历,那么问题来了,“不重复”该如何解决?     我之前就陷入了一个错误的思维,就是认为一旦发现某个元素再次出现,就意味着要进行新一轮的长度计算了。而事实上,正确的思维应该是一旦发现某一个元素再次出现,就应该从该元素上次出现位置的下一个位置起进行新一轮的长度计算!于是我们要找一个变量记录上次出现的位置,这就是vec出现的原因。

接下来再分析一个稍微差一点的算法:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if (s.empty()) {
            return 0;
        }
        if (s.size() == 1) {
            return 1;
        }
        
        int count = 0;
        int max_substr = 0;
        vector<int> vec(256, 0);
        int i = 0; 
        int j = 0;
        vec[s[i]]++;

        while (j < s.size()) {
            //cout << "====" << endl;
            if (count == 0) {
                max_substr = max(max_substr, j - i + 1);
                //cout << "got one: " << max_substr << endl;
            }
            
            j++;
            if (vec[s[j]] == 0) {
                //cout << s[j] << " not seen before, record"<< endl;
                vec[s[j]]++;
            } else { // have repeat
                // record this repeat, and how bad it is in terms of violation count
                //cout << s[j] << " repeat!"<< endl;
                if (vec[s[j]] == 1) {
                    count++;
                }
                vec[s[j]]++;
                //cout << "count: " << count << endl;
                
                while (count > 0 && i <= j) {
                // before that, remove what's in i
                    vec[s[i]]--; // from multiple to 1
                    if (vec[s[i]] == 1) {
                        count--;
                    }
                    //cout << "count: " << count << endl;
                    i++;
                }
            }
        }
        
        return max_substr;
    }
};

这个算法首先对特殊情况进行处理,

以"abcade"为例测试,运行结果如下:

====
got one: 1
b not seen before, record
====
got one: 2
c not seen before, record
====
got one: 3
a repeat!
count: 1
count: 0
====
got one: 3
d not seen before, record
====
got one: 4
e not seen before, record
====
got one: 5
  not seen before, record
5


什么鬼!!!!!想不通

做完本题的收获:

1.见识到了一个记录是否访问过的技巧;

2.见识到了vector的新用法,即vector<int> vec(256,1); vec['a']=3;这样


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值