LeetCode brushing day 6 (string arrangement)

Posted by jblack on Wed, 29 Dec 2021 08:45:06 +0100

subject

Give you two strings s1 and s2 and write a function to determine whether s2 contains the arrangement of s1.
In other words, one of the permutations of s1 is a substring of s2.
Example 1:

Input: s1 = "ab" s2 = "eidbaooo"
Output: true
 Explanation: s2 contain s1 One of the permutations of ("ba").

Example 2:

Input: s1= "ab" s2 = "eidboaoo"
Output: false

Method 1: sliding window

thought

Because the arrangement does not change the number of characters in a string, one string is the arrangement of the other only when the number of characters in each of the two strings is equal.

According to this property, remember that the length of s1 is n, and we can traverse each substring with length N in s2 to judge whether the number of substrings is equal to that of each character in s1. If they are equal, it means that the substring is an arrangement of s1.

Use two arrays cnt1 and cnt2, cnt1\textit{cnt}_1cnt1 , count the number of characters in s1, and cnt2 , count the number of characters in the currently traversed substring.

Since the length of the substring to be traversed is n, we can use a sliding window with a fixed length of n to maintain cnt2: each time the sliding window slides to the right, we count more characters entering the window and less characters leaving the window. Then, it is determined whether cnt1 and cnt2 are equal. If they are equal, it means s1s_ One of the permutations of 1S1 is the substring of s2.

JAVA code

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m) {
            return false;
        }
        int[] cnt1 = new int[26];
        int[] cnt2 = new int[26];
        for (int i = 0; i < n; ++i) {
            ++cnt1[s1.charAt(i) - 'a'];
            ++cnt2[s2.charAt(i) - 'a'];
        }
        if (Arrays.equals(cnt1, cnt2)) {
            return true;
        }
        for (int i = n; i < m; ++i) {
            ++cnt2[s2.charAt(i) - 'a'];
            --cnt2[s2.charAt(i - n) - 'a'];
            if (Arrays.equals(cnt1, cnt2)) {
                return true;
            }
        }
        return false;
    }
}

Method 2: optimize sliding window

Optimization thought

Note that each time the window slides, only two characters, one in and one out, are counted, but the entire cnt1 and cnt2 arrays are compared.

From this point of view, we can use a variable diff to record the number of different values of cnt1 , and cnt2 , so that judging whether cnt1 and cnt2 are equal is transformed into judging whether diff is 0

Each time the window slides, remember one in and one out as x and y.

If x=y, it has no effect on cnt2 and can be skipped directly.

If x ≠ y, for character x, if cnt2[x]=cnt1[x] exists before modifying CNT2, add diff by one; Modifying CNT2 \ textit {CNT}_ If there is cnt2[x]=cnt1[x] after 2cnt2 , subtract diff by one. The character y is the same.

In addition, to simplify the above logic, we can use only one array CNT, where cnt[x]=cnt2[x] − cnt1[x], and replace the comparison between cnt1[x] and cnt2[x] with the comparison between cnt[x] and 0.

JAVA code

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m) {
            return false;
        }
        int[] cnt = new int[26];
        for (int i = 0; i < n; ++i) {
            --cnt[s1.charAt(i) - 'a'];
            ++cnt[s2.charAt(i) - 'a'];
        }
        int diff = 0;
        for (int c : cnt) {
            if (c != 0) {
                ++diff;
            }
        }
        if (diff == 0) {
            return true;
        }
        for (int i = n; i < m; ++i) {
            int x = s2.charAt(i) - 'a', y = s2.charAt(i - n) - 'a';
            if (x == y) {
                continue;
            }
            if (cnt[x] == 0) {
                ++diff;
            }
            ++cnt[x];
            if (cnt[x] == 0) {
                --diff;
            }
            if (cnt[y] == 0) {
                ++diff;
            }
            --cnt[y];
            if (cnt[y] == 0) {
                --diff;
            }
            if (diff == 0) {
                return true;
            }
        }
        return false;
    }
}

Complexity analysis

Time complexity: O(n+m+∣Σ∣),among n Is a string s1​ The length of the, m Is a string s2​ The length of the,Σ Is the character set. The character set in this question is lowercase letters,∣Σ∣=26. 
Space complexity: O(∣Σ∣). 

Method 3: double pointer

Algorithmic thought

Reviewing the idea of method 1, we investigate whether there is an interval so that the value of cnt is all 0 under the condition of ensuring that the interval length is n.

Conversely, we can also check whether there is an interval whose length is exactly n under the condition of ensuring that the value of cnt is not positive.

Initially, only the characters in s1 are counted, then the cnt values are not positive, and the sum of element values is − n.

Then, the two pointers left and right are used to represent the investigated interval [left,right]. Right every time you move to the right, the character x entering the interval is counted. In order to ensure that the cnt value is not positive, if cnt[x] > 0 at this time, move the left pointer to the right and reduce the cnt value of characters leaving the interval until cnt[x] ≤ 0.

Note that every time the length of [left,right] increases by 1, the sum of the element values of cnt increases by 1. When the length of [left,right] is exactly n, it means that the sum of element values of cnt is 0. Since the value of cnt is not positive, the sum of element values is 0, which means that all elements are 0, so we find a target substring.

JAVA code

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m) {
            return false;
        }
        int[] cnt = new int[26];
        for (int i = 0; i < n; ++i) {
            --cnt[s1.charAt(i) - 'a'];
        }
        int left = 0;
        for (int right = 0; right < m; ++right) {
            int x = s2.charAt(right) - 'a';
            ++cnt[x];
            while (cnt[x] > 0) {
                --cnt[s2.charAt(left) - 'a'];
                ++left;
            }
            if (right - left + 1 == n) {
                return true;
            }
        }
        return false;
    }
}

Complexity analysis

Time complexity: O(n+m+∣Σ∣). 
establish cnt need O(∣Σ∣) Time of day.
ergodic s1​ need O(n) Time of day.
Double pointer traversal s2​ Due to left and right Will only move to the right, so this part needs to O(m) Time of day.
Space complexity: O(∣Σ∣). 

Link: https://leetcode-cn.com/problems/permutation-in-string/solution/zi-fu-chuan-de-pai-lie-by-leetcode-solut-7k7u/
Source: LeetCode

Topics: Java Algorithm data structure leetcode pointer