Leetcode hot100 438 || 找到字符串中所有字母异位词

题目

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

示例 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" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 10^4
  • s 和 p 仅包含小写字母

我的思路

  1. 判断特殊情况,当s为空或者p为空时,返回空,s的长度小于p的长度时返回空
  2. 对p中的字母进行统计,因为p全为小写字母,用一个大小为26的数组进行统计
  3. 设置初始指针start和end为0和p.length-1,形成窗口,统计窗口中字母数量,和p的统计数组进行对比,出现一个不同则结束对比,完全相同则将此时的start索引加入结果列表,然后,更新s窗口统计数组,原来start所指字母的数量减1,start和end右移一个位置,新的end所指的字母数量加1
  4. 不断对比,至end到s的末端
  5. 返回结果列表

代码

java

package q438_find_anagrams;

import java.util.ArrayList;
import java.util.List;

public class FindAnagrams {
    public List<Integer> findAnagrams(String s, String p) {
//        判断特殊情况,当s为空或者p为空时,返回空,s的长度小于p的长度时返回空
        List<Integer> result=new ArrayList<>();
        if(s.isEmpty() || p.isEmpty() || s.length()<p.length()){
            return result;
        }

//        对p中的字母进行统计,因为p全为小写字母,用一个大小为26的数组进行统计
        char[] charp=p.toCharArray();
        int[] countp=new int[26];
        for (int i = 0; i < charp.length; i++) {
            countp[charp[i]-'a']++;
        }
//        设置初始指针start和end为0和p.length-1,形成窗口,
        int start=0;
        int end=charp.length-1;
        char[] chars=s.toCharArray();
//        统计窗口中字母数量,和p的统计数组进行对比,
        int k=0;
        int[] counts=new int[26];
        for (int j = start; j <=end; j++) {
            counts[chars[j]-'a']++;
        }
        while (end<chars.length){
            //出现一个不同则结束对比,
            k=start;
            while (k<=end){
                if(countp[chars[k]-'a']!=counts[chars[k]-'a']){
                    break;
                }
                k++;
            }
            // 完全相同则将此时的start索引加入结果列表,
            if(k==end+1){
                result.add(start);
            }
            // 然后,更新s窗口统计数组,原来start所指字母的数量减1,start和end右移一个位置,新的end所指的字母数量加1
            counts[chars[start]-'a']--;
            start++;
            end++;
            if(end<chars.length){
                counts[chars[end]-'a']++;
            }
        }


//                返回结果列表
        return result;
    }

    public static void main(String[] args) {
        String s="cbaebabacd";
        String p="abc";
        FindAnagrams fa=new FindAnagrams();
        List<Integer> result=fa.findAnagrams(s,p);
        System.out.println(result);
    }
}

结果:时间击败18.27%,空间击败39.20%

根据官方题解优化后代码:

  1. 不用把字符串转换成字符数组,直接使用charAt方法获取对应位置的字符内容
  2. 将初始化p和s窗口中字符数量数组的过程由两个循环合并成一个循环
  3. 改变p和s窗口中字符数量统计数组的比较方式,直接使用Arrays的equals方法进行对比,能大幅缩短时间
优化后代码
package q438_find_anagrams;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FindAnagrams {
    public List<Integer> findAnagrams(String s, String p) {
//        判断特殊情况,当s为空或者p为空时,返回空,s的长度小于p的长度时返回空
        List<Integer> result=new ArrayList<>();
        if(s.isEmpty() || p.isEmpty() || s.length()<p.length()){
            return result;
        }

//        对p中的字母进行统计,因为p全为小写字母,用一个大小为26的数组进行统计
        int[] countp=new int[26];
        int[] counts=new int[26];
        for (int i = 0; i < p.length(); i++) {
            countp[p.charAt(i)-'a']++;
            counts[s.charAt(i)-'a']++;
        }
//        设置初始指针start和end为0和p.length-1,形成窗口,
        int start=0;
        int end=p.length()-1;
//        统计窗口中字母数量,和p的统计数组进行对比,
        while (end<s.length()){
            if(Arrays.equals(counts,countp)){
                result.add(start);
            }

            // 然后,更新s窗口统计数组,原来start所指字母的数量减1,start和end右移一个位置,新的end所指的字母数量加1
            counts[s.charAt(start)-'a']--;
            start++;
            end++;
            if(end<s.length()){
                counts[s.charAt(end)-'a']++;
            }
        }


//                返回结果列表
        return result;
    }

    public static void main(String[] args) {
        String s="cbaebabacd";
        String p="abc";
        FindAnagrams fa=new FindAnagrams();
        List<Integer> result=fa.findAnagrams(s,p);
        System.out.println(result);
    }
}

Python

  1. 定义给定大小的整型数组:arr=[0]*size
  2. 数组对比,直接用 ==
  3. 字符转换成ascii码:ord()
class FindAnagrams():
    def findAnagrams(self,s,p):
        result=[]
        # 判断特殊情况
        if(len(s)<len(p)):
            return result
        # 统计初始窗口和p的各字母数量
        pCount=[0]*26
        sCount=[0]*26
        for i in range(len(p)):
            pCount[ord(p[i])-ord('a')]+=1
            sCount[ord(s[i])-ord('a')]+=1

        # 对比s窗口和p的统计结果
        for i in range(len(s)-len(p)+1):
            if pCount==sCount:
                result.append(i)
            sCount[ord(s[i])-ord('a')]-=1
            if i+len(p)<len(s):
                sCount[ord(s[i+len(p)])-ord('a')]+=1

        return result

s="abab"
p="ab"
fa=FindAnagrams()
print(fa.findAnagrams(s,p))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值