leetcode
leetcode 2901 ~ 2950
字符串的排列

字符串的排列

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 54 ms, 内存: 16.0 MB


// 思路:使用Java Stream来实现滑动窗口算法判断s2是否包含s1的某个变位词
// 1. 创建一个数组来存储s1的字符频率
// 2. 使用Stream API来遍历s2的字符,并在一个滑动窗口内维持字符频率
// 3. 如果窗口的大小大于s1的长度,则移除窗口最左边的字符
// 4. 比较窗口内的字符频率和s1的字符频率是否相同,相同则返回true

import java.util.stream.IntStream;
import java.util.stream.Collectors;
import java.util.Arrays;

public class Solution {
    public boolean checkInclusion(String s1, String s2) {
        if (s1.length() > s2.length()) return false;

        int[] s1Map = new int[26];
        int[] s2Map = new int[26];

        s1.chars().forEach(c -> s1Map[c - 'a']++);
        IntStream.range(0, s1.length()).forEach(i -> s2Map[s2.charAt(i) - 'a']++);

        return IntStream.range(0, s2.length() - s1.length() + 1)
                .anyMatch(i -> {
                    if (i > 0) {
                        s2Map[s2.charAt(i - 1) - 'a']--;
                        s2Map[s2.charAt(i + s1.length() - 1) - 'a']++;
                    }
                    return Arrays.equals(s1Map, s2Map);
                });
    }
}

解释

方法:

这个题解采用了滑动窗口的方法来判断s2是否包含s1的某个变位词作为子串。首先,通过一个长度为26的数组s1_count来统计s1中每个字符的出现次数。数组的索引对应从'a'到'z'的每个字符。接着,使用同样的方式,通过另一个长度为26的数组s2_count来动态地在s2上维护一个长度为s1长度的滑动窗口的字符出现次数。随着窗口在s2上从左到右滑动,检查当前窗口内的字符分布是否与s1相同。如果在任何时刻两个数组相等,说明s2中存在一个子串是s1的变位词,返回True。如果遍历完s2后没有找到任何匹配,返回False。

时间复杂度:

O(m)

空间复杂度:

O(1)

代码细节讲解

🦆
在滑动窗口算法中,如何确保s2_count数组在窗口移动过程中始终保持精确反映当前窗口内的字符频率?
在滑动窗口算法中,s2_count数组通过两种操作来保证其精确性:增加和减少字符计数。当窗口向右扩展时,新进入窗口的字符的计数会增加。当窗口左侧的字符移出窗口时,该字符的计数会相应减少。这样,s2_count数组始终精确地反映了当前窗口内各字符的频率。
🦆
题解中提到比较两个数组s1_count和s2_count是否相等的操作是O(1),但实际上不需要遍历整个数组来进行比较吗?
实际上,比较两个数组是否相等确实需要遍历整个数组,因此这个操作的时间复杂度是O(26),即O(1)。这里的O(1)表示常数时间复杂度,因为无论输入大小如何,比较的时间都是固定的,由于数组长度总是26,所以可以认为是常数时间。
🦆
如果s1的长度大于s2的长度,这种情况下的返回值是什么?题解中是否考虑了这一边界情况?
如果s1的长度大于s2的长度,则不存在任何可能的窗口可以包含s1的排列,因为窗口的大小至少需要与s1的长度相等。在这种情况下,算法应该立即返回False。题解中虽未明确说明这一点,但在算法实现中,这种情况会自然地处理,因为窗口无法扩展到大于或等于s1长度,从而不会进入主要的比较逻辑。

相关问题