438. 找到字符串中所有字母异位词

题目

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明

  • 字母异位词指字母相同,但排列不同的字符串。
  • 不考虑答案输出的顺序。

示例1

输入:
s: "cbaebabacd" p: "abc"

输出:
[0, 6]

解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。

示例2

输入:
s: "abab" p: "ab"

输出:
[0, 1, 2]

解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。

思路

  • 字符串s和p,找到s中连续的的几个字符,每个字符的数量等于p。
  • 使用两个数组need和window。need负责记录p中每个字符的数量。window用来记录一个位于s上的窗口中,存在的字符是否满足need。
  • 使用left和right作为window的左右端点。
    • 先扩展window的右边,如果right处的字符数量已经满足了need,则说明当前window中的一个字符数量已经匹配need中该字符的数量,用match表示,match增加一。不停的向右扩展,直到match的数量达到need中的字符类数。
    • 再收缩左边界,将不需要考虑的字符排出窗口。如果left处的字符是need中的,那么在window中该字符的数量减一,如果不再满足need,那么match减一,停止收缩,继续向右扩展。如果match数满足,并且window区间的长度等于p,那么久找到了一个字母异位词。

代码

可以使用哈希表,也可以使用数组,但是哈希表比较慢,下面两种都列出来。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int size = 0;   // p中有几种字符
        int left = 0, right = 0;    // 滑动窗口的左右边界

        int need[128] = { 0 };
        int window[128] = { 0 };
        
        vector<int> res;
        int match = 0;

        for ( auto& c : p ) // 计算需求
            ++need[c];
        
        for ( int i = 0; i < 128; ++i ) // 计算有几种字符
            if ( need[i] != 0 )
                ++size;
        
        while ( right < s.size() ) {    // 循环停止条件为右边界到最后
            char c1 = s[right];         // 先扩展有边界

            if ( need[c1] != 0 ) {      // 如果右边界的字符在需求中。
                ++window[c1];

                if ( window[c1] == need[c1] )   // 如果该字符满足需求,match增一
                    ++match;
            }

            ++right;    // 右边界增一

            while ( match == size ) {           // 收缩左边界知道窗口元素不再满足需求
                if ( right - left == p.size() ) // 如果窗口中不含有其他字符
                    res.push_back( left );      // 则左边界满足要求
                
                char c2 = s[left];

                if ( need[c2] != 0 ) {          // 如果左边界在需求中
                    --window[c2];               // 窗口减去该元素

                    if ( window[c2] < need[c2] )    // 少于需求,减少match
                        --match;
                }

                ++left;
            }
        }

        return res;
    }
};

哈希表

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char, int> need;
        unordered_map<char, int> window;
        vector<int> res;
        int match = 0;

        for ( auto& c : p )
            ++need[c];
        
        int left = 0, right = 0;
        while ( right < s.size() ) {
            char c1 = s[right];

            if ( need.count( c1 ) ) {
                ++window[c1];

                if ( window[c1] == need[c1] )
                    ++match;
            }

            ++right;

            while ( match == need.size() ) {
                if ( right - left == p.size() )
                    res.push_back( left );
                
                char c2 = s[left];

                if ( need.count( c2 ) ) {
                    --window[c2];

                    if ( window[c2] < need[c2] )
                        --match;
                }

                ++left;
            }
        }

        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值