[Java小白的leetcode做题记录] 438. 找到字符串中所有字母异位词

[Java]LeetCode-438-找到字符串中所有字母异位词

题目描述1

给定一个字符串 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" 的字母异位词。

我的想法(失败)

字符串的匹配与否,最先想到的是暴力循环,一共嵌套了4个循环,若s的长度为m,p的长度为n,则时间复杂度为 O ( m n 3 ) O(mn^3) O(mn3)

遍历s到其剩余字符数少于p的字符数时可停止,每次遍历在s中从当前位置i延选与p长度相同的区间,开始一一比较,当区间内字符与p中可一一对应时,则将当前位置i加入返回列表。在区间比较中,我设置了一个长度为p的长度的位置数组,来存储已匹配的字符位置 ,当下一次比较字符与这次相同时就可跳过该位置,避免重复。

但因该方法复杂度过高,在34/36实例超时挂掉。

//结果失败,倒在了34/36实例,超时
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> fa = new ArrayList<>();	 //申请一个获得最终存储索引的返回列表
        int i = 0;	//s索引初始化为0
        
        for(i = 0; i < s.length() - p.length() + 1; i++)	//当s中剩余字符数少于p字符数时停止
        {
            int count = 0;	//当前区间s与p的匹配数
            int[] x = new int[p.length()];	//记录p中已于s匹配的位置,使得下次查找跳过该位置,不重复
            for(int c = 0; c < p.length(); c++)
            {
                x[c] = -1;	//初始为-1
            }
            for(int j = 0; j < p.length(); j++)	//从s的当前索引i遍历p的长度,与p比较
            {
                for(int k = 0; k < p.length(); k++)	//让p从头与s[i+j]一一比较
                {
                    if(s.charAt(i+j) == p.charAt(k))	//当前s与p匹配时
                    {
                        x[j] = k;	//记录已匹配位置
                        int count1 = 0;	//记录当前字符位置相异数(避免重复值的干扰)
                        for(int b = 0; b < p.length(); b++)
                        {
                            if(k != x[b])	//若当前匹配位置没有出现过
                            {
                                count1++;
                            }
                        }
                        if(count1 == p.length()-1)	//若当前字符匹配且与之前位置不重复,则count1值应为p的长度减1
                        {
                            count++;
                            break;
                        }
                    }
                }   
            }
            if(count == p.length())	//s与p的匹配数相同时,将索引i加入返回列表中
            {
                fa.add(i);
            }
        }
        return fa;
    }
}

别人的想法2

用滑动窗口思想(链接中有图解)

  1. 计算字符串p中各个元素出现的次数,用数组记录
  2. 用变量start表示窗口的起始位置,变量end表示窗口的结束位置,区间[start,end]用于记录当前窗口中的元素。
    扩大窗口右侧边界的条件是:字符串s还有剩余元素未考察且窗口[start,end]内的字符长度小于字符串p的长度;
    缩小窗口左侧边界的条件是:窗口[start,end]内字符的长度等于字符串p的长度。
  3. 窗口[start,end]内字符的长度等于字符串p的长度时,接着要做的就是判断窗口内的字符串是不是字符串p的字母异位词,比较窗口内各元素的出现次数和字符串p中各元素的出现次数是否一样,一样就对,不一样就错(每个字母出现次数相同,只是顺序不同)。

很妙,时间复杂度为 O ( m n ) O(mn) O(mn)

public List<Integer> findAnagrams(String s, String p) {
    List<Integer> resultList = new ArrayList<>();// 计算字符串p中各元素的出现次数
    int[] pFreq = new int[26];
    for(int i = 0; i < p.length(); i++) {
        pFreq[p.charAt(i)-'a']++;
    }// 窗口区间为[start,end]
    int start = 0, end = -1;
    while (start <s.length()) {
        // 还有剩余元素未考察,且窗口内字符串长度小于字符串p的长度
        // 则扩大窗口右侧边界
        if (end+1 < s.length() && end-start+1 <p.length()) {
            end++;
        }else {
            // 右侧边界不能继续扩大或窗口内字符串长度等于字符串p的长度
            // 则缩小左侧边界
            start++;
        }// 当窗口内字符串长度等于字符串p的长度时,则判断其是不是字符串p的字母异位词子串
        if (end-start+1 == p.length() && isAnagrams(s.substring(start,end+1), pFreq)) {
            resultList.add(start);
        }
    }
    return resultList;
}// 判断当前子串是不是字符串p的字母异位词
private boolean isAnagrams(String window, int[] pFreq) {
    // 计算窗口内字符串各元素的出现次数
    int[] windowFreq = new int[26];
    for(int i = 0; i < window.length(); i++) {
        windowFreq[window.charAt(i)-'a']++;
    }// 比较窗口内各元素的出现次数和字符串p中各元素的出现次数是否一样
    // 如果都一样,则说明窗口内的字符串是字符串p的字母异位词子串
    // 如果不一样,则说明不是其子串
    for(int j = 0; j < 26; j++) {
        if (windowFreq[j] != pFreq[j]) {
            return false;
        }
    }
    return true;
}

参考链接


  1. 来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 ↩︎

  2. 作者:hardcore-aryabhata
    链接:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/solution/hua-dong-chuang-kou-438-zhao-dao-zi-fu-c-ut38/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值