數組喇完、雙指針、滑動窗口剥啤、夾逼锦溪、前綴數組


三數之和

給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a府怯,b刻诊,c ,使得 a + b + c = 0 牺丙?請你找出所有滿足條件且不重復的三元組则涯。
注意:答案中不可以包含重復的三元組。

示例:
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[[-1, 0, 1],[-1, -1, 2]]

解法一:排序+夾逼(推薦)
1)首先對數組進行排序(涉及數的大小一般都會進行排序處理)粟判,有助于去重的實現(xiàn)肖揣;
2)排序后固定一個數 nums[i],再使用左右指針浮入,對后面的數組進行夾逼遍歷(nums[L]和nums[R])龙优,計算三個數的和 sum 判斷是否滿足為 0,滿足則添加進結果集
3)如果 nums[i]大于 0事秀,則三數之和必然無法等于 0彤断,結束循環(huán);
如果 nums[i] == nums[i-1]易迹,則說明該數字重復宰衙,會導致結果重復,所以應該跳過睹欲;
當 sum == 0 時供炼,nums[L] == nums[L+1]則會導致結果重復,應該跳過窘疮,L++袋哼;
當 sum == 0時,nums[R] == nums[R-1] 則會導致結果重復闸衫,應該跳過涛贯,R--;

時間復雜度:O(n^2 )蔚出,n 為數組長度

public static List<List<Integer>> threeSum2(int[] nums) {
        List<List<Integer>> ans = new ArrayList();
        int len = nums.length;
        if (nums == null || len < 3) {
            return ans;
        }
        Arrays.sort(nums); // 排序
        for (int i = 0; i < len; i++) {
            if (nums[i] > 0) {
                break; // 如果當前數字大于0弟翘,則三數之和一定大于0,所以結束循環(huán)
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue; // 去重
            }
            int L = i + 1;
            int R = len - 1;
            while (L < R) {
                int sum = nums[i] + nums[L] + nums[R];
                if (sum == 0) {
                    ans.add(Arrays.asList(nums[i], nums[L], nums[R]));
                    while (L < R && nums[L] == nums[L + 1]) L++; // 去重
                    while (L < R && nums[R] == nums[R - 1]) R--; // 去重
                    L++;
                    R--;
                } else if (sum < 0){
                    L++;
                } else if (sum > 0) {
                    R--;
                }
            }
        }
        return ans;
    }

解法二:二重暴力+Hash(Slow)
1)暴力解法為三重for循環(huán)遍歷所有情況骄酗,考慮到第三個數可以由前兩個數得到稀余,使用HashMap可以避免第三重循環(huán),從而達到O(n^2)復雜度
2)用Set<List<Integer>>去重趋翻,前提是list里的元素的順序一致睛琳;

private List<List<Integer>> hashSolution(int[] nums) {
       if (nums == null || nums.length <= 2) {
            return Collections.emptyList();
        }
        Set<List<Integer>> results = new LinkedHashSet<>();

        for (int i = 0; i < nums.length - 2; i++) {
            Set<Integer> hashSet = new HashSet<>();
            for (int j = i + 1; j < nums.length; j++) {
                int d = -nums[i] - nums[j];
                if (hashSet.contains(d)) {
                    List<Integer> list = Arrays.asList(nums[i], d, nums[j]);
                    //  排序, 否則三個相同的數, 但位置不同, 無法去重
                    list.sort(Comparator.naturalOrder());
                    results.add(list);
                } else {
                    hashSet.add(nums[j]);
                }
            }
        }
        return new ArrayList<>(results);
    }

四數之和


雙指針
1)注意去重的代碼
2)剪枝可節(jié)省時間

    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        int n = nums.length;
        Arrays.sort(nums);
        for (int i = 0; i <= n - 4; i++) {
            // 去重
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            // 剪枝
            if(nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target){
                return ans;
            }
            if(nums[n-1] + nums[n-2] + nums[n-3] + nums[n - 4] < target){
                return ans;
            }
            for (int j = i + 1; j <= n - 3; j++) {
                // 去重
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                // 剪枝
                if(nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target){
                    continue;
                }
                if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] < target){
                    continue;
                }
                int left = j + 1;
                int right = n - 1;
                while (left < right) {
                    // 去重
                    while (left < right && left > j + 1 && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (left < right && right < n - 1 && nums[right] == nums[right + 1]) {
                        right--;
                    }
                    if(left < right){
                        int sum = nums[i] + nums[j] + nums[left] + nums[right];
                        if (sum == target) {
                            ans.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                            left++;
                            right--;
                        } else if (sum > target) {
                            right--;
                        } else {
                            left++;
                        }
                    }
                }
            }
        }
        return ans;
    }

和為k的子數組

給定一個整數數組和一個整數 k,你需要找到該數組中和為 k 的連續(xù)的子數組的個數嘿歌。
示例 1 :
輸入:nums = [1,1,1], k = 2
輸出: 2 , [1,1] 與 [1,1] 為兩種不同的情況掸掏。

解法一:滑動窗口
1)先固定一個數nums[start]茁影,然后移動另一個指針宙帝,求和即可
2)依次移動固定數的位置,重復上述過程
兩個指針募闲,兩輪遍歷步脓,算法復雜度為O(N^2)

class Solution {
    public int subarraySum(int[] nums, int k) {
        int n = nums.length;
        int count = 0;

        int lastSum = 0;
        for (int start = 0; start < n; start++) {
            for (int end = start; end < n; end++) {
               if(end == start){
                   if(nums[start] == k){
                       count ++;
                   }
                   lastSum = nums[end];
               }else {
                   lastSum += nums[end];
                   if(lastSum == k){
                       count ++;
                   }
               }
            }
        }

        return count;
    }
}

解法二:HashMap+前綴和(推薦)
1)考慮到此題只需給出滿足條件的子序列的數量,而不需要具體的子序列:可使用一個map記錄所有從0開始的子序列的sum值及其出現(xiàn)次數,通過長序列的sum減去短序列的sum可以得到中間子序列的sum值靴患,所以就不需要兩個指針了仍侥。
2)建立map表用于存儲每個從零項開始的連續(xù)子數組的sum求和及其出現(xiàn)的次數,初始化為(0,1)鸳君,表示和為0的連續(xù)子數組(空數組)出現(xiàn)1次农渊。
3)sum的值是在對nums數組的遍歷中不斷累加當前元素的,res的值則需要查找map中是否已存在sum-k的元素或颊,也就是在查找此前所有從0項開始累加的連續(xù)子項和中有沒有sum-k砸紊。因為:長序列sum-短序列sum=sum-(sum-k)=k如果有的話,則說明從該項到當前項的連續(xù)子數組和必定為k囱挑,那么res則可以和這個sum的對應值醉顽,即這個sum出現(xiàn)的次數,相加得到新的res平挑。
算法復雜度為O(N)游添。

class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        int sum = 0, ret = 0;       
        // sum為0的子序列出現(xiàn)一次,即空序列通熄;
        map.put(0, 1);

        for(int i = 0; i < nums.length; ++i) {
            sum += nums[i];
            if(map.containsKey(sum-k))
                ret += map.get(sum-k);
            map.put(sum, map.getOrDefault(sum, 0)+1);
        }
        
        return ret;
    }
}

和可被 K 整除的子數組

給定一個整數數組 A唆涝,返回其中元素之和可被 K 整除的(連續(xù)、非空)子數組的數目唇辨。
示例:
輸入:A = [4,5,0,-2,-3,1], K = 5
輸出:7
解釋:
有 7 個子數組滿足其元素之和可被 K = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
提示:
1 <= A.length <= 30000
-10000 <= A[i] <= 10000
2 <= K <= 10000

解法:前綴和+同余定理
1)通常涉及連續(xù)子數組的時候石抡,可以使用前綴和來解決。連續(xù)子數組的求和助泽,可以使用前綴和的差來得到啰扛。前綴和:Sum(i)=num[0] + num[1] + ... + num[i],任意連續(xù)子數組的求和:Sum(i,j)=Sum(j) - Sum(i-1)嗡贺。
2)同余定理:Sum(i,j)\%K=0(Sum(j) - Sum(i-1))\%K=0隐解,根據同余定理得到:Sum(j)\%K=Sum(i-1)\%K
3)根據上面的結論诫睬,我們使用一個數組記錄前綴和對K取模的結果的數量煞茫。即取模結果為0~K-1的各個值的數量。假設對K取模值為r的前綴和共有n個摄凡,那么這n個前綴和中任意取2個续徽,即得到一個滿足條件的子數組,所以這種情況下亲澡,共有C_{n}^{2}=\frac{n*(n-1)}{2}個滿足條件的子數組钦扭。將所有的取模值r,計算求和即可得到最后的解床绪。注意:對于取模值r為0的情況客情,比較特殊其弊,除了任選兩個前綴和的組合情況之外, 每個前綴和本身就是一個滿足條件的子數組,所以假設有n個取模值r為0的前綴和膀斋,對應情況的子數組個數為:n+C_{n}^{2}梭伐。

 public static int subarraysDivByK(int[] A, int K) {
        int ret = 0;

        int[] counts = new int[K];

        // 前綴和
        int sum = 0;
        for (int i = 0; i < A.length; i++) {
            sum += A[i];
            // 保證對K取模的結果都為正數0~K-1, Java中-2%6=-2, 我們希望的值是-2%6=4
            int mod = sum % K;
            if(mod < 0){
                mod += K;
            }
            counts[mod] += 1;
        }

        for (int i = 0; i < K; i++) {
            int count = counts[i];
            if (i == 0) {
                ret += count + count * (count - 1) / 2;
            } else {
                ret += count * (count - 1) / 2;
            }
        }

        return ret;
    }

無重復字符的最長子串

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

示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因為無重復字符的最長子串是 "abc"糊识,所以其長度為 3。
示例 2:
輸入: "bbbbb"
輸出: 1
解釋: 因為無重復字符的最長子串是 "b"摔蓝,所以其長度為 1技掏。
示例 3:
輸入: "pwwkew"
輸出: 3
解釋: 因為無重復字符的最長子串是 "wke",所以其長度為 3项鬼。

滑動窗口
依次遞增地枚舉子串的起始位置哑梳,那么子串的結束位置也是遞增的!這里的原因在于绘盟,假設我們選擇字符串中的第 k個字符作為起始位置鸠真,并且得到了不包含重復字符的最長子串的結束位置為 rk。那么當我們選擇第 k+1 個字符作為起始位置時龄毡,首先從 k+1 到 rk的字符顯然是不重復的吠卷,并且由于少了原本的第 k 個字符,我們可以嘗試繼續(xù)增大 rk 沦零,直到右側出現(xiàn)了重復字符為止祭隔。

這樣以來,我們就可以使用「滑動窗口」來解決這個問題了:

我們使用兩個指針表示字符串中的某個子串(的左右邊界)路操。其中左指針代表著上文中「枚舉子串的起始位置」疾渴,而右指針即為上文中的 rk;

在每一步的操作中屯仗,我們會將左指針向右移動一格搞坝,表示 我們開始枚舉下一個字符作為起始位置,然后我們可以不斷地向右移動右指針魁袜,但需要保證這兩個指針對應的子串中沒有重復的字符桩撮。在移動結束后,這個子串就對應著 以左指針開始的峰弹,不包含重復字符的最長子串店量。我們記錄下這個子串的長度;在枚舉結束后鞠呈,我們找到的最長的子串的長度即為答案融师。

 public static int lengthOfLongestSubstring2(String s) {

        int n = s.length();
        if (n <= 1) {
            return n;
        }
        int maxLen = 1;
        Set<Character> chars = new HashSet<>();

        int R = 0;
        for(int L = 0; L<n; L++){
            if(L > 0){
                chars.remove(s.charAt(L-1));
            }

            while (R < n && !chars.contains(s.charAt(R))){
                chars.add(s.charAt(R));
                R++;
            }

            maxLen = Math.max(maxLen, R-L);
        }

        return maxLen;
    }

或者事實上L每次不止可以加1

public static int lengthOfLongestSubstring(String s) {

        int n = s.length();
        if (n <= 1) {
            return n;
        }

        int maxLen = 1;
        Set<Character> chars = new HashSet<>();

        int L = 0;
        int R = 0;
        while (L < n && R < n){
            char c = s.charAt(R);
            if(chars.contains(c)){
                maxLen = Math.max(maxLen, R-L);
                while (s.charAt(L) != c){
                    chars.remove(s.charAt(L));
                    L++;
                }
                L++;
            }
            chars.add(c);
            R++;
        }

        maxLen = Math.max(maxLen, R-L);

        return maxLen;

    }

數組中重復的數據(https://leetcode-cn.com/problems/find-all-duplicates-in-an-array/)

給定一個整數數組 a,其中1 ≤ a[i] ≤ n (n為數組長度), 其中有些元素出現(xiàn)兩次而其他元素出現(xiàn)一次粟按。
找到所有出現(xiàn)兩次的元素诬滩。
你可以不用到任何額外空間并在O(n)時間復雜度內解決這個問題嗎霹粥?
示例:
輸入:
[4,3,2,7,8,2,3,1]
輸出:
[2,3]

利用索引號+取反
利用索引號灭将,因為所有數值是在1~n之間疼鸟,那么我們可以用索引0表示數字1,索引1表示數字2...庙曙。每遍歷一次數組空镜,就將此數的index對應的原來正的數字取負,若此index對應的數為負捌朴,則說明已出現(xiàn)過吴攒;并且取負之后仍然可以取到正確的位置值(取絕對值即可)。

public static List<Integer> findDuplicates(int[] nums) {

        List<Integer> ans = new ArrayList<>();

        for (int num : nums){
            int index = Math.abs(num)-1;
            if(nums[index] < 0){
                ans.add(Math.abs(num));
            }
            nums[index] = -nums[index];
        }

        return ans;
    }

最長湍流子數組

當 A 的子數組 A[i], A[i+1], ..., A[j] 滿足下列條件時砂蔽,我們稱其為湍流子數組:
若 i <= k < j洼怔,當 k 為奇數時, A[k] > A[k+1]左驾,且當 k 為偶數時镣隶,A[k] < A[k+1];
或 若 i <= k < j诡右,當 k 為偶數時安岂,A[k] > A[k+1] ,且當 k 為奇數時帆吻, A[k] < A[k+1]域那。
也就是說,如果比較符號在子數組中的每個相鄰元素對之間翻轉猜煮,則該子數組是湍流子數組次员。
返回 A 的最大湍流子數組的長度。

示例 1:
輸入:[9,4,2,10,7,8,8,1,9]
輸出:5
解釋:(A[1] > A[2] < A[3] > A[4] < A[5])
示例 2:
輸入:[4,8,12,16]
輸出:2
示例 3:
輸入:[100]
輸出:1

滑動窗口

public static int maxTurbulenceSize(int[] A) {

        if (A.length <= 1) {
            return A.length;
        }

        int ans = 1;
        int start = 0;
        for(int i=1; i<A.length; i++){
            int c = Integer.compare(A[i-1], A[i]);
            if(i == A.length-1 || c * Integer.compare(A[i], A[i+1]) != -1){
                if(c != 0){ // 避免所有數都一樣時, 答案不為1
                    ans = Math.max(ans, i-start+1);
                }
                start = i;
            }
        }

        return ans;
    }

替換后的最長重復字符(https://leetcode-cn.com/problems/longest-repeating-character-replacement/)

給你一個僅由大寫英文字母組成的字符串王带,你可以將任意位置上的字符替換成另外的字符翠肘,總共可最多替換 k 次。在執(zhí)行上述操作后辫秧,找到包含重復字母的最長子串的長度束倍。
注意:
字符串長度 和 k 不會超過 104。
示例 1:
輸入:
s = "ABAB", k = 2
輸出:
4
解釋:
用兩個'A'替換為兩個'B',反之亦然盟戏。
示例 2:
輸入:
s = "AABABBA", k = 1
輸出:
4
解釋:
將中間的一個'A'替換為'B',字符串變?yōu)?"AABBBBA"绪妹。
子串 "BBBB" 有最長重復字母, 答案為 4。

滑動窗口
本題是比較典型的滑動窗口問題

這類問題一般通過一個滑動窗口就能在O(N)的時間復雜度下求解

本題可以先退化成考慮K=0的情況柿究,此時題目就變成了求解字符串中最長連續(xù)子串長度問題了

我們先可以通過這個特例先了解一下滑動窗口的求解過程

上圖的求解過程展示中邮旷,窗口從左至右不斷擴張/滑動,當窗口觸達字符串末尾字符時蝇摸,運算結束婶肩,窗口的寬度為最終結果办陷。初始窗口的寬度為1,我們不斷的通過向當前窗口覆蓋的子串后面追加一個字符看是否能滿足我們的要求律歼,如果滿足窗口擴張民镜,如果不滿足,窗口向右滑動险毁。

當K>0時制圈,子串的條件變成了允許我們變換子串中的K個字符使其變成一個連續(xù)子串

那么這個題的關鍵點就是我們如何判斷一個字符串改變K個字符,能夠變成一個連續(xù)串

如果當前字符串中的出現(xiàn)次數最多的字母個數 + K >= 串長度畔况,那么這個串就是滿足條件的

我們維護一個數組int[26]來存儲當前窗口中各個字母的出現(xiàn)次數鲸鹦,left表示窗口的左邊界,right表示窗口右邊界

窗口擴張:left不變跷跪,right++
窗口滑動:left++, right++

public static int characterReplacement(String s, int k) {

        int n = s.length();
        if (n <= 1 || n <= k + 1) {
            return n;
        }

        int[] charCount = new int[26];
        int L = 0;
        int R = 0;
        charCount[s.charAt(R) - 'A']++;
        int maxCount = 1;

        while (R < n) {

            if(maxCount + k >= R - L + 1){
                // 窗口擴大
                R++;
                if (R < n) {
                    int index = s.charAt(R) - 'A';
                    charCount[index]++;
                    maxCount = Math.max(maxCount, charCount[index]);
                }

            }else {
                // 窗口移動
                charCount[s.charAt(L) - 'A']--;
                L++;

                R++;
                if (R < n) {
                    int index = s.charAt(R) - 'A';
                    charCount[index]++;
                    maxCount = Math.max(maxCount, charCount[index]);// 真實值 <= 這里的值, 不過不影響結果的正確性
                }
            }
        }

        return R - L;
    }

上述代碼可以簡化為

public static int characterReplacement(String s, int k) {

        int n = s.length();
        if (n <= 1 || n <= k + 1) {
            return n;
        }

        int[] charCount = new int[26];
        int L = 0;
        int R = 0;
        charCount[s.charAt(R) - 'A']++;
        int maxCount = 1;

        while (R < n) {
            // 窗口移動
            if(maxCount + k < R - L + 1){
                charCount[s.charAt(L) - 'A']--;
                L++;
            }

            // 窗口擴大
            R++;
            if (R < n) {
                int index = s.charAt(R) - 'A';
                charCount[index]++;
                maxCount = Math.max(maxCount, charCount[index]);
            }
        }

        return R - L;
    }

16. 最接近的三數之和](https://leetcode-cn.com/problems/3sum-closest/)

給定一個包括 n 個整數的數組 nums和 一個目標值 target馋嗜。找出 nums中的三個整數,使得它們的和與 target 最接近吵瞻。返回這三個數的和葛菇。假定每組輸入只存在唯一答案。

示例:
輸入:nums = [-1,2,1,-4], target = 1
輸出:2
解釋:與 target 最接近的和是 2 (-1 + 2 + 1 = 2) 听皿。

提示:

  • 3 <= nums.length <= 10^3
  • -10^3 <= nums[i] <= 10^3
  • -10^4 <= target <= 10^4

雙指針

public static int threeSumClosest(int[] nums, int target) {

        Arrays.sort(nums);

        int ans = nums[0] + nums[1] + nums[2];
        for(int fix = 0; fix < nums.length; fix ++){
            int L = fix + 1;
            int R = nums.length - 1;
            while (L < R){
                int sum = nums[fix] + nums[L] + nums[R];
                if(Math.abs(sum - target) < Math.abs(ans - target)){
                    ans = sum;
                }
                if(sum - target > 0){
                    R--;
                }else if(sum == target){
                    return target;
                }else {
                    L++;
                }
            }
        }
        return ans;
    }

長度最小的子數組


雙指針(滑動伸縮窗口)
技巧總結:
1)求連續(xù)子數組長度熟呛,自然聯(lián)想到雙指針;
2)涉及連續(xù)子數組的和尉姨,可以聯(lián)想到前綴數組(暴力解法中才會用到庵朝,雙指針解法中不用);
3)雙指針的實現(xiàn):兩個指針+while循環(huán)。(或者for循環(huán)中嵌套while循環(huán))
時間復雜度為O(n)又厉,因為每個元素最多遍歷兩遍九府,左指針一遍,右指針一遍覆致。

public int minSubArrayLen(int s, int[] nums) {
        int n = nums.length;
        if(n == 0){
            return 0;
        }
        int minLen = 0;
        int L = 0;
        int R = 0;
        int sum = nums[R];
        while (L <= R && R < n){
            if(sum >= s){
                int len = R - L + 1;
                minLen = minLen == 0 ? len : Math.min(len, minLen);
                sum -= nums[L++];
            }else{
                R ++;
                if(R < n){
                    sum += nums[R];
                }
            }
        }
        return minLen;
    }

顏色分類


計數排序

    public void sortColors(int[] nums) {
        int[] count = new int[3];
        for(int num : nums){
            count[num] ++;
        }
        int index = 0;
        for(int i=0; i<count.length; i++){
            while (count[i]-- > 0){
                nums[index++] = i;
            }
        }
    }

多指針

  • curr指針侄旬,遍歷數組,將0元素放在最左邊煌妈,將2元素放在最右邊儡羔,1元素繼續(xù);通過交換元素操作璧诵;
  • p0指針汰蜘,指向左邊0元素的最右邊界;
  • p2指針之宿,指向右邊2元素的最左邊界族操;
    public void sortColors(int[] nums) {
        int curr = 0;
        int p0 = 0;
        int p2 = nums.length-1;
        while (curr < p2){
            if(nums[curr] == 0){
                swap(nums, p0, curr);
                p0 ++;
                curr++;
            }else if(nums[curr] == 1){
                curr++;
            }else {
                swap(nums, p2, curr);
                p2--;
            }
        }
    }

    public void swap(int[] nums, int index1, int index2){
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }


字符串的排列


數組匹配

  • 我們可以使用數組來存儲各字符的出現(xiàn)頻率,然后判斷兩個數組是否相等來判斷兩個長度相同的字符串是否互為排列比被。給定字符串僅包含小寫字母('a'到'z')色难。因此我們需要采用大小為 26 的數組泼舱。
    public boolean checkInclusion(String s1, String s2) {
        int len = s1.length();
        int[] count1 = getCount(s1);
        for (int i = 0; i <= s2.length() - len; i++) {
            String sub = s2.substring(i, i + len);
            if (match(count1, getCount(sub))) {
                return true;
            }
        }
        return false;
    }

    public int[] getCount(String s) {
        int[] count = new int[26];
        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }
        return count;
    }

    public boolean match(int[] count1, int[] count2) {
        for (int i = 0; i < 26; i++) {
            if (count1[i] != count2[i]) {
                return false;
            }
        }
        return true;
    }

滑動窗口優(yōu)化

  • 在上述的過程中,在對s2進行滑動的時候枷莉,都重新計算了子串的計數數組娇昙,其實沒有必要,因為相鄰的數組之間只有微笑的區(qū)別依沮,只需要計算第一個窗口的計數數組涯贞,后面每移動一步枪狂,做出相應改變即可危喉。
    public boolean checkInclusion(String s1, String s2) {
        int len = s1.length();
        int[] count1 = getCount(s1);
        if (s2.length() < s1.length()) {
            return false;
        }
        int[] count2 = getCount(s2.substring(0, len));
        if (match(count1, count2)) {
            return true;
        }
        for (int i = 1; i <= s2.length() - len; i++) {
            count2[s2.charAt(i - 1) - 'a']--;
            count2[s2.charAt(i + len - 1) - 'a']++;
            if (match(count1, count2)) {
                return true;
            }
        }
        return false;
    }

    public int[] getCount(String s) {
        int[] count = new int[26];
        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }
        return count;
    }

    public boolean match(int[] count1, int[] count2) {
        for (int i = 0; i < 26; i++) {
            if (count1[i] != count2[i]) {
                return false;
            }
        }
        return true;
    }

字母異位詞分組


跟上題類似

  • 將count數組映射成一個字符串key:用"#"連接所有數字即可。
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> ans = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        for (String str : strs) {
            int[] count = getCount(str);
            String countKey = getCountKey(count);
            if(map.containsKey(countKey)){
                map.get(countKey).add(str);
            }else {
                List<String> list = new ArrayList<>();
                list.add(str);
                map.put(countKey, list);
            }
        }
        for(List<String> list : map.values()){
            ans.add(list);
        }
        return ans;
    }

    public String getCountKey(int[] count) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            sb.append("#").append(count[i]);
        }
        return sb.toString();
    }

    public int[] getCount(String s) {
        int[] count = new int[26];
        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }
        return count;
    }

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末州疾,一起剝皮案震驚了整個濱河市辜限,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌严蓖,老刑警劉巖薄嫡,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異颗胡,居然都是意外死亡毫深,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門毒姨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哑蔫,“玉大人,你說我怎么就攤上這事弧呐≌⒚裕” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵俘枫,是天一觀的道長腥沽。 經常有香客問我,道長鸠蚪,這世上最難降的妖魔是什么今阳? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮茅信,結果婚禮上盾舌,老公的妹妹穿的比我還像新娘。我一直安慰自己汹押,他們只是感情好矿筝,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棚贾,像睡著了一般窖维。 火紅的嫁衣襯著肌膚如雪榆综。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天铸史,我揣著相機與錄音鼻疮,去河邊找鬼。 笑死琳轿,一個胖子當著我的面吹牛判沟,可吹牛的內容都是我干的。 我是一名探鬼主播崭篡,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼挪哄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琉闪?” 一聲冷哼從身側響起迹炼,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颠毙,沒想到半個月后斯入,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡蛀蜜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年刻两,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滴某。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡磅摹,死狀恐怖,靈堂內的尸體忽然破棺而出壮池,到底是詐尸還是另有隱情偏瓤,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布椰憋,位于F島的核電站厅克,受9級特大地震影響,放射性物質發(fā)生泄漏橙依。R本人自食惡果不足惜证舟,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窗骑。 院中可真熱鬧女责,春花似錦、人聲如沸创译。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刷喜,卻和暖如春残制,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掖疮。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工初茶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浊闪。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓恼布,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搁宾。 傳聞我的和親對象是個殘疾皇子折汞,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361