算法题解题记录——Longest Substring Without Repeating Characters(leetCode#3-medium)

本文由作者三汪首发于简书。
历史解题记录已同步更新github.

改进版solution提交结果.png

题目

Problem Description:
Given a string, find the length of the longest substring without repeating characters.

Examples:

  • Given "abcabcbb", the answer is "abc", which the length is 3.
  • Given "bbbbb", the answer is "b", with the length of 1.
  • Given "pwwkew", the answer is "wke", with the length of 3.
    Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

题目分析

题意是让我们返回给定字符串不含重复字符的最大子字符串的长度。

代码和思路

我会对自己的原始提交版本做个记录。
没耐心看的同学可以跳过原始版本直接去看后面的B和C,
分别是我对当前Java solution最优解的分析和我自己的改进版本。

A.原始版本

看到这个题目的时候,我是把它当成字符串处理题来做的。
没注意看题目要求返回的是符合要求的最大子字符串长度。
因此第一个版本做了很多字符处理,Runtime也很感人,用了90ms,排位在12.61%的位置。

1.0版本代码如下:
    private int lengthOfLongestSubstring_1_0(String str){
        if (str==null || str.equals("")) {
            return 0;
        }
        String[] subs = new String[str.length()];
        int subsIndex = 0;
        String[] chars = str.split("");
        StringBuffer sb = new StringBuffer(chars[0]);
        for (int i = 1; i < chars.length; i++) {
            if (!sb.toString().contains(chars[i])) {
                sb.append(chars[i]);
            }else if(sb.indexOf(chars[i]) != sb.length()-1){
                subs[subsIndex++] = sb.toString();
                sb.delete(0,sb.indexOf(chars[i])+1);
                sb.append(chars[i]);
            }else{
                subs[subsIndex++] = sb.toString();
                sb.delete(0, sb.length());
                sb.append(chars[i]);
            }
        }
        subs[subsIndex++] = sb.toString();
        int maxLength = subs[0].length();
        for (int i = 1; i < subs.length; i++) {
            if (subs[i] == null) {
                return maxLength;
            }
            if (subs[i].length() > maxLength) {
                maxLength = subs[i].length();
            }
        }
        return maxLength;
    }
思路分析:

这段代码的思路是先把给定字符串分解成单个字符数组,再依次写入StringBuffer。

当出现重复字符时,根据重复字符在字符串中的位置分两种情况处置。
情况一、重复字符不是当前字符串的最后一个字符时:
将当前StringBuffer中拼好的字符串写入String[]数组,删除从字符串开端到重复字符位置的所有字符,从重复字符的后一个字符为字符串的开端,继续拼接字符串。
情况二、重复字符是当前字符串的最后一个字符时:
将当前StringBuffer中拼好的字符串写入String[]数组,清空StringBuffer,重新开始新的字符串拼接。

最后遍历String[]数组,获取长度最大的字符串的长度并返回。

不足之处:

在这个版本的代码中我有一个很大的弊病是我忘了String可以字节调用.toCharArray()方法获取char[]数组,而是用了.split("")来把给定字符串分割成一个个由单个字符组成的字符串。

B.改进版本

改进版本不再使用字符串拼接的方法来实现。
而是使用一个快指针(Runner)和一个慢指针(Walker)来遍历由给定字符串分解的char[]数组,直接获取最大子字符串长度。
这种方式Runtime只有36ms,排位在99.94%的位置。

值得一提的是,这个改进版本是在看了一会儿会分析的当前Java最优解以后结合自己思考而来的。
我通过写自己的改进版来理解最优解的代码思路。这是一个小心得,你也可以试试。

1.1版本代码如下:
    private int lengthOfLongestSubstring_1_1(String str){
        //It's my own habit to check if the input is empty.
        if (str == null) {
            return 0;
        }
        char[] chars = str.toCharArray();
        if (chars.length<2) {
            return chars.length;
        }
        int maxLength = 0,spliter = 0;
        for (int i = 1; i < chars.length; i++) {
            for (int j = spliter ; j < i ; j++) {
                if (chars[i]==chars[j]) {
                    int tempLength = i-spliter;
                    maxLength = maxLength > tempLength ? maxLength : tempLength;
                    spliter = j+1;
                    break;
                }
            }
        }
        maxLength = maxLength > chars.length-spliter ? maxLength : chars.length-spliter;
        return maxLength;
    }
思路分析:
  1. 当给定字符串长度小于2时,直接返回它的长度。
  2. 使用快指针和慢指针遍历给定字符串分解而来的char[]数组
    2.1 使用spliter来记录重复字符的下一个字符的位置(重复字符和它前面的字符对新子字符串没有意义),初始值为0。
    2.2 使用maxLength来记录每次遍历时子字符串的最大长度。当有新的最大子字符串产生(遇到重复字符或结束遍历)时,判断其长度是否大于maxLength。若是,则更新maxLength。
  3. 我不知道我真的解释清楚了没,虽然我觉得我解释清楚了。如果正在看的你哪里不理解,欢迎留言讨论:)

C.截至此刻LeetCode上用时最少的Java Solution

代码如下:
    /**
     * 
     * <p>
     * Description:The best solution on LeetCode by this time.<br />
     * Runtime:35ms<br />
     * </p>
     * @version 0.1 2017年12月8日
     * @param s
     * @return
     * int
     */
    private int lengthOfLongestSubstring_best(String s) {
        char[] chars = s.toCharArray();
        if(2 > chars.length){
            return chars.length;
        }
        int max = 0;
        int split_at = 0;
        int cur_len = 1;
        for(int i=1;i<chars.length;i++){
            int j = split_at;
            for(;j<i;j++){
                if(chars[i] == chars[j]){
                    break;
                }
            }
            if(j < i){
                split_at = j+1;
                cur_len = i-j;
            }else{
                cur_len++;
            }
            if(cur_len > max) max = cur_len;
        }
        return max;
    }
思路分析:
  1. 主体思路同改进版本思路,下面来讲一些细节方面的不同。
  2. 使用cur_len字段来记录当前子字符串长度,初始值为1。
    使用split_at字段来记录上一次碰到的重复字符的下一个字符的位置,初始值为0。
    使用max字段记录每次遍历时子字符串的最大长度。
  3. 当出现重复字符时,break跳出快指针循环(即内层for循环,快指针为j)。
    结束内层for循环以后,判断快指针与慢指针(慢指针为i)是否相等。
    当出现重复字符时,快指针与慢指针是不会相等的。因为会break跳出循环。
    3.1 若快指针与慢指针相等,则当前子字符串没有遇到重复字符。cur_len自增。
    3.2 若快指针小于慢指针,则不含重复字符的当前子字符串长度为cur_len=i-j,更新split_at字段。
    3.3 快指针不可能大于慢指针。因为内层for循环条件是j<i
  4. 每次结束外层循环时,判断cur_len是否大于max。若是,则更新max。
  5. 我不知道我真的解释清楚了没,虽然我觉得我解释清楚了。如果正在看的你哪里不理解,欢迎留言讨论:)

以上。
希望我的文章对你能有所帮助。
我不能保证文中所有说法的百分百正确,
但我能保证它们都是我的理解和感悟以及拒绝直接复制黏贴(确实需要引用的部分我会附上源地址)。
有什么意见、见解或疑惑,欢迎留言讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容