LeetCode03 - 無重複字符的最長子串(Java 實現)

LeetCode03 - 無重複字符的最長子串(Java 實現)

來源:力扣(LeetCode)
連接:https://leetcode-cn.com/problems/longest-substring-without-repeating-charactersjava

題目描述

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。數組

示例 1:數據結構

輸入: "abcabcbb"
輸出: 3 
解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。

示例 2:ui

輸入: "bbbbb"
輸出: 1
解釋: 由於無重複字符的最長子串是 "b",因此其長度爲 1。

示例 3:code

輸入: "pwwkew"
輸出: 3
解釋: 由於無重複字符的最長子串是 "wke",因此其長度爲 3。
     請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。

相關知識點補充

Java 數組的下標能夠是:leetcode

  • 整型常量
  • 整型變量
  • 整型字符常量(字符的 ASCII 碼)
  • 整型表達式

例如:字符串

int a[300],i;
// 下標是整型常量,整數 5
a[5] = 5; 

// 下標是整型變量, i 如今數值 5, 就是數組元素 a[5]。
i = 5;
a[i] = 5; 

// 下標是整型字符常量,等於 F 的 ASCII 碼值,就是數組元素 a[70]。
a['F'] = 5; 

// 下標是整型表達式,表達式運算結果是 5,就是數組元素 a[5]。
a['F' - 'A'] = 5;

怎麼查看 ASCII 碼的值?get

// 好比查看 a 的 ASCII 值
System.out.println((int)'a');
// 結果爲 97

System.out.println((int)'z');
// 結果爲 122

Java 實現與實現思路

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 03:給定一個字符串,請你找出其中不含有重複字符的最長子串的長度。
 *
 * @author XiaoPengwei
 * @since 2019-07-14
 */
public class LC03LongestSubstring {

    public static void main(String[] args) {

        // 該表明性示例,最長爲 4,即 abcd
        // String str = "abcabcdbcs";
        String str = "abba";

        int lengthByMethod1 = lengthOfLongestSubstringMethod1(str);
        System.out.println("lengthByMethod1-->" + lengthByMethod1);

        int lengthByMethod2 = lengthOfLongestSubstringMethod2(str);
        System.out.println("lengthByMethod2-->" + lengthByMethod2);
    }

    /**
     * 方法一:
     * 求不含有重複字符的最長子串的長度
     * 思路:建立一個 pre 數組表示長度,從左到右遍歷字符串數組,查看
     *
     * @param s 字符串參數
     * @return int 長度
     */
    public static int lengthOfLongestSubstringMethod1(String s) {

        // 數組沒有賦值的時,全部元素會初始化爲 0
        // 字符爲下標時,會將 ASCII 碼做爲下標
        int[] pre = new int[128];

        int max = 0, t = 0;

        // i 表示當前處理的第 i 個字符
        for (int i = 0; i < s.length(); i++) {

            // c 爲依次取出的單個字符
            char c = s.charAt(i);

            /* 若是 pre[c] 不等於 0 表示數組中該位置被修改過,也就表明前面有重複字符
             */
            if (pre[c] != 0 && pre[c] > t) {

                // 更新 max 最大值
                // i - t 重複元素下標 - 上一次沒重複的下標
                max = Math.max(max, i - t);

                // t 是爲求下一個子串長度作準備,由於要求出的是最長的子串長度
                // 更新 t,上一次沒重複的下標
                t = pre[c];
            }

            // 若是 pre[c] 爲 0,或者 pre[c] <= t
            pre[c] = i + 1;
        }

        return Math.max(max, s.length() - t);
    }

    /**
     * 方法二:
     * 定義一個 map 數據結構存儲 (k, v),其中 key 值爲字符,value 值爲字符位置 +1,加 1 表示從字符位置後一個纔開始不重複
     * 咱們定義不重複子串的開始位置爲 start,結束位置爲 end; [start, end] 可理解爲滑動窗口
     * 隨着 end 不斷遍歷向後,會遇到與 [start, end] 區間內字符相同的狀況,此時將字符做爲 key 值,獲取其 value 值,並更新 start,此時 [start, end] 區間內不存在重複字符
     * 不管是否更新 start,都會更新其 map 數據結構和結果 max。
     * 時間複雜度:O(n)
     *
     * @param s 字符串參數
     * @return int 長度
     * @author guanpengchn
     * @link https://leetcode-cn.com/problems/two-sum/solution/hua-jie-suan-fa-3-wu-zhong-fu-zi-fu-de-zui-chang-z/
     */
    public static int lengthOfLongestSubstringMethod2(String s) {

        // max 表示所求的最大長度
        int max = 0;

        /* Map 中 key 值爲字符,value 值爲字符位置 +1,加 1 表示從字符位置後一個纔開始不重複
         * 同一個字符 key,在 map 中只存在一個。當重複時更新它的值。
         */
        Map<Character, Integer> map = new HashMap<Character, Integer>();

        /* start 指向不重複子串的第一個字符的下標。
         * 當存在重複字符時 start 指向後面一個重複字符,指向下一個不重複子串的第一個字符的下標
         */
        for (int start = 0, end = 0; end < s.length(); end++) {

            char c = s.charAt(end);

            if (map.containsKey(c)) {

                /* 若是含有重複字符串,將滑動窗戶的開始位置更新,後移
                 * map.get(c) 的值表示該出現重複的字符,上一次出現時下標+1
                 * 何時會出現 start<map.get(c)?
                 * 答:map.get(c) 該重複字符出現的位置不必定,若是重複字符出現的順序和以前同樣,
                 * 好比:abcabcd,先重 a,再重 b
                 * 何時會出現 start>map.get(c)?
                 * 答:第二次出現重複時,若是重複的字符在第一次出現重複的字符前面
                 * 好比 abba,先重 b,再重 a。出現 a 重複時,start 爲 2 > map.get('a') 爲 1
                 */
                start = Math.max(map.get(c), start);
            }

            /* 無論是否重複這裏都會執行
             * 每次執行判斷一次滑動窗口長度是否超過當前最大長度,是則更新
             * end - start + 1 表示滑動窗口長度,子串長度,不重複子串最短也爲 1
             * 爲何要 + 1?
             * 當 start 和 end 指向一個是元素時,下標同樣,end-start 爲 0,此時存在一個不重複子串爲 1 個元素
             * 當 end 指向 start 後面相鄰一個,end-start 爲 1,此時不重複子串爲 2 個元素
             */
            max = Math.max(max, end - start + 1);

            /* 無論是否重複這裏都會執行
             * 若是不重複,會新添加一個 key,值爲:位置下標+1
             * 若是重複,會更新這個 key 對應的值爲:後面又重複出現的該字符的位置下標+1
             */
            map.put(c, end + 1);
        }

        return max;
    }
}
相關文章
相關標籤/搜索