《LeetCode之每日一题》:87.字符串的排列

Posted 是七喜呀!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《LeetCode之每日一题》:87.字符串的排列相关的知识,希望对你有一定的参考价值。

字符串的排列


题目链接: 字符串的排列

有关题目

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,第一个字符串的排列之一是第二个字符串的 子串 。
示例 1:

输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
示例 2:

输入: s1= "ab" s2 = "eidboaoo"
输出: False
提示:

输入的字符串只包含小写字母
两个字符串的长度都在 [1, 10,000] 之间

题解

法一:滑动窗口

思路:
子串必须长度相同,我们维护长度为s2。length的在s1中的子串,
使用滑动窗口,判断是否存在子串间排列关系
bool equals(int* cnt1, int* cnt2) {
    for (int i = 0; i < 26; i++) {
        if (cnt1[i] != cnt2[i]) {
            return false;
        }
    }
    return true;
}

bool checkInclusion(char * s1, char * s2){
    int n = strlen(s1), m = strlen(s2);
    if (n > m) {
        return false;
    }//特判
    int cnt1[26], cnt2[26];
    memset(cnt1,0,sizeof(cnt1));
    memset(cnt2,0,sizeof(cnt2));
    for (int i = 0; i < n ; ++i){
        ++cnt1[s1[i] - 'a'];
        ++cnt2[s2[i] - 'a'];
    }
    if (equals(cnt1, cnt2)) {
        return true;
    }
    for (int i = n; i < m; ++i){
        ++cnt2[s2[i] - 'a'];//窗口滑动
        --cnt2[s2[i - n] - 'a'];//少统计一个出窗口的字符
        if (equals(cnt1, cnt2)) {
            return true;
        }
    }
    return false;
}

法二:滑动窗口优化

思路:
注意到每次窗口滑动时,只统计了一进一出两个字符,却比较了整个cnt1 与cnt2 数组
我们使用diff来记录cnt1 与 cnt2的不同值的个数,若
diff==0,则满足题意
每次窗口滑动,记一进一出两个字符为x 和 y,讨论x与y之间的相等关系
此外,为简化上述逻辑,我们可以只用一个数组 
cnt[x] = cnt2[x] - cnt1[x]
将cnt1[x] 与cnt2[x]之间的大小关系转换为cnt[x]0之间的比较
bool checkInclusion(char * s1, char * s2){
    int n = strlen(s1), m = strlen(s2);
    if (n > m) {
        return false;
    }
    int cnt[26];
    memset(cnt, 0, sizeof(cnt));

    for (int i = 0; i < n; ++i) {
        --cnt[s1[i] - 'a'];
        ++cnt[s2[i] - 'a'];
    }

    int diff = 0;
    for (int i = 0; i < 26; ++i) {
        if (cnt[i] != 0) {
            ++diff;
        }
    }
    if (diff == 0) {
        return true;
    }
    
    for (int i = n; i < m; ++i) {
        int x = s2[i] - 'a', y = s2[i - n] - 'a';
        if (x == y) {
            continue;
        }
        if (cnt[x] == 0) {//x进之前,满足字符相同
            ++diff;//不同字符+1
        }
        ++cnt[x];
        if (cnt[x] == 0) {//x进之后,满足字符相同
            --diff;//不同字符-1
        }
        if (cnt[y] == 0) {//y出,相类似
            ++diff;
        }
        --cnt[y];
        if (cnt[y] == 0) {
            --diff;
        }
        if (diff == 0) {
            return true;
        }
    }
    return false;
}

在这里插入图片描述

法三:双指针

思路:
法一与二控制窗口长度为n,看是否满足区间使得cnt值全为0
双指针,反过来,保证cnt不为正的情况下,考察一个区间是否
长度为n
//①:cnt[x] = cnt2[x] - cnt1[x] 为正,则
说明这个字符,s1并没有那么多,要窗口左移,不然肯定不会有cnt为正的时机,剔除left上的s2字符
//②:如果不大于,说明start字符在s2的出现次数还没达到s1的标准,窗口要扩张。纳入新字符

如果遍历s2结束,始终没有返回真,说明怎么也找不到,返回假
bool checkInclusion(char * s1, char * s2){
    int n = strlen(s1), m = strlen(s2);
    if (n > m) {
        return false;
    }

    int cnt[26];
    memset(cnt, 0, sizeof(cnt));
    for (int i = 0; i < n; ++i) {
        --cnt[s1[i] - 'a'];
    }
    
    int left = 0;
    for (int right = 0; right < m; ++right) {
        int x = s2[right] - 'a';
        ++cnt[x];
        while (cnt[x] > 0) {
            --cnt[s2[left] - 'a'];
            ++left;
        }
        if (right - left + 1 == n) {
            return true;
        }
    }
    return false;
}

在这里插入图片描述

以上是关于《LeetCode之每日一题》:87.字符串的排列的主要内容,如果未能解决你的问题,请参考以下文章

《LeetCode之每日一题》:191.全排列

《LeetCode之每日一题》:174.排列硬币

《LeetCode之每日一题》:192.全排列 II

《LeetCode之每日一题》:94.面试题 10.02. 变位词组

《LeetCode之每日一题》:92.减小和重新排列数组后的最大元素

《LeetCode之每日一题》:251.一手顺子