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

题目

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

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

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

示例

 

思路

创造不易,不喜勿喷,个人见解,有错误欢迎指点

看到题目的第一想法:一个大字符串(s)里面找小字符串(p),并且没有顺序要求,首先考虑滑动窗口,我们定义窗口大小为小字符串的长度p_len,在大字符串(s)中移动滑动窗口,每次移动一个字符,每次移动之后,拿滑动窗口和p比较里面字符是否一致,不要求位置一样,这里比较如果要求位置一样那就简单,遍历两个字符串比较就可以了,但是现在位置可以不要求一样,那就需要用哈希表来辅助了,我们创建一个哈希表,将滑动窗口和p加入,然后比较两个哈希表是否一样即可

优化

我们之前每次都需要对滑动窗口和小字符串p创建哈希表,但是可以发现滑动窗口是变化的,但是小字符串p是不变的,那么我们就可以创建一次哈希表,保存p的值,然后反复和滑动窗口进行比较,这样子就可以减少对不变p进行反复操作

再优化

我们之前对小字符串p进行了优化,只对p进行了一次遍历,但是还是对s每一步都需要对滑动窗口进行哈希表转换,那么是不是有一种方法可以让减少对滑动窗口的哈希表转换了,或者不是每一步都需要去反复遍历整个滑动窗口,当然是有的,我们之前都是滑动窗口是不变的,那么我们引入双指针来维护这个滑动窗口,一个是窗口前指针,一个是窗口后。当访问元素是p中元素,并且这个元素还可以添加到滑动窗口中,即这个元素出现的次数小于p中该元素出现的次数,那么我们使窗口大小加1,并且使窗口前指针前进,添加新的元素,当访问元素不是p中元素,或者这个元素在滑动窗口中出现的次数大于了在p中出现的次数,那么我们就缩小窗口大小,因为这个窗口多了一个元素,我们使窗口的尾指针排出元素,排到什么为止,当然是把多的这个元素排出去为止。因为每个元素在p中出现的次数是一定的,我们控制的窗口大小又始终保存出现元素个数小于等于p中出现的次数,那么当窗口大小等于p的大小时,是不是就说明我们这个窗口是p的异位词了

你品!你细品!

拜拜结束!!!

喜欢就点个赞宝!!!

代码

第一次

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
/*
*int strCmpn:比较滑动窗口和字符串的相同值
char * s:字符串s,滑动窗口的位置
char * p:字符串p,比较字符串
int n:滑动窗口的起始位置
int p_len:字符串p的长度
返回值:相同返回0,不相同返回不相同元素个数
*/
int strCmpn(char * s, char * p , int n , int p_len)
{
    int * a = malloc(sizeof(int) * 26);//滑动窗口哈希表初始化为0
    int * b = malloc(sizeof(int) * 26);//p哈希表初始化为0
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    int i = n;
    int j = 0;
    for(i,j; j < p_len; i++, j++)//对滑动窗口,p进行遍历赋值
    {
        int t_a = s[i] - 'a';
        int t_b = p[j] - 'a';
        a[t_a]++;
        b[t_b]++;
    }
    int sum = 0;
    for(j = 0; j < 26; j++)//相同返回sum为0,不相同返回不相同元素
    {
        sum += abs(a[j] - b[j]);
    }
    free(a);
    free(b);
    return sum; 
}
/*
*int* findAnagrams:寻找p在s中元素相同的子字符串
char * s:比较字符串
char * p:目标字符串
int* returnSize:返回值长度
返回值:相同元素的初始下标
*/
int* findAnagrams(char * s, char * p, int* returnSize){
    int i;
    int p_len = strlen(p);
    int s_len = strlen(s);
    *returnSize=0;
    if(s_len < p_len)
    {
        return NULL;
    }
    int * ans = malloc(sizeof(int) * 30000);
    for(i = 0; i < s_len-p_len+1; i++)//移动滑动窗口
    {
        if(strCmpn(s, p, i, p_len) == 0)
        {
            ans[(*returnSize)++] = i;
        }
    }
    return ans;
}

优化 

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
/*
*int strCmpn:比较滑动窗口和字符串的相同值
char * s:字符串s,滑动窗口的位置
char * p:字符串p,比较字符串
int n:滑动窗口的起始位置
int p_len:字符串p的长度
返回值:相同返回0,不相同返回不相同元素个数
*/
int strCmpn(char * s, int * b , int n , int p_len)
{
    int a[26] = {0};//滑动窗口哈希表初始化为0
    int i = n;
    for(i; i < p_len+n; i++)//对滑动窗口进行遍历赋值
    {
        a[s[i] - 'a']++;
    }
    int sum = 0;
    for(i = 0; i < 26; i++)//相同返回sum为0,不相同返回不相同元素
    {
        sum += abs(a[i] - b[i]);
    }
    return sum; 
}
/*
*int* findAnagrams:寻找p在s中元素相同的子字符串
char * s:比较字符串
char * p:目标字符串
int* returnSize:返回值长度
返回值:相同元素的初始下标
*/
int* findAnagrams(char * s, char * p, int* returnSize){
    int i;
    int p_len = strlen(p);
    int s_len = strlen(s);
    *returnSize=0;
    if(s_len < p_len)
    {
        return NULL;
    }
    int b[26] = {0};//对p的哈希表初始化为0
    for(i = 0; i < p_len; i++)//对p进行遍历赋值
    {
        b[p[i] - 'a']++;
    }
    int * ans = malloc(sizeof(int) * 30000);
    for(i = 0; i < s_len-p_len+1; i++)//移动滑动窗口
    {
        if(strCmpn(s, b, i, p_len) == 0)
        {
            ans[(*returnSize)++] = i;
        }
    }
    return ans;
}

再优化 

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
/*
*int* findAnagrams:寻找p在s中元素相同的子字符串
char * s:比较字符串
char * p:目标字符串
int* returnSize:返回值长度
返回值:相同元素的初始下标
*/
int* findAnagrams(char * s, char * p, int* returnSize){
    int i,j;
    int p_len = strlen(p);
    int s_len = strlen(s);
    *returnSize=0;
    if(s_len < p_len)
    {
        return NULL;
    }
    int a[26] = {0};
    int b[26] = {0};
    int len = 0;
    for(i = 0; i < p_len; i++)//对p进行遍历赋值
    {
        b[p[i] - 'a']++;
    }
    int * ans = malloc(sizeof(int) * 30000);
    for(i = 0, j = 0; i < s_len && j < s_len; )//移动滑动窗口
    {
        if(a[s[i] - 'a'] < b[s[i] - 'a'])//当前位置的字符是p中元素,并且还可以再添加起码一个
        {
            a[s[i] - 'a']++;//
            len++;//滑动窗口大小加一
            i++;//进行向滑动窗口添加元素
            if(len == p_len)//滑动窗口的长度正好等于p的长度,保存i的初始值
            {
               ans[(*returnSize)++] = i-p_len; //i的初始值等于当前位置-滑动窗口大小+1
            }
            
        }
        else//当前元素不是p中元素或者是p中元素,但是这个元素多了
        {
            //我们要缩小滑动窗口,起码把当前这个多的元素踢出我们滑动窗口
            len--;//滑动窗口减少了一个元素,大小也需要减少1
            a[s[j] - 'a']--;
            j++;
        }
        //printf("%d\n",len);
    }
    return ans;
}

时间空间复杂度

第一次

优化

 

再优化

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用滑动窗口和哈希表来解决这个问题。具体步骤如下: 1. 定义一个哈希表,用于记录目标字符串每个字符出现的次数。 2. 定义两个指针 left 和 right,分别表示滑动窗口的左右边界。 3. 初始化 left 和 right 为 0,表示滑动窗口的大小为 0。 4. 遍历目标字符串,每次将 right 指针向右移动一格,并将对应的字符出现次数加一。 5. 如果 right - left == p.length(),说明滑动窗口的大小已经达到了目标字符串 p 的长度,此时需要判断滑动窗口的字符是否是 p 的一个字母异位。 6. 判断方法是比较滑动窗口的字符出现次数和 p 每个字符出现次数是否一致,如果一致则说明找到了一个字母异位。 7. 将 left 指针向右移动一格,同时将对应的字符出现次数减一,继续遍历目标字符串。 Java 代码实现如下: ``` public List<Integer> findAnagrams(String s, String p) { List<Integer> result = new ArrayList<>(); if (s == null || s.length() == 0 || p == null || p.length() == 0) { return result; } int[] hash = new int[26]; for (char c : p.toCharArray()) { hash[c - 'a']++; } int left = 0, right = 0, count = p.length(); while (right < s.length()) { if (hash[s.charAt(right) - 'a'] > 0) { count--; } hash[s.charAt(right) - 'a']--; right++; if (count == 0) { result.add(left); } if (right - left == p.length()) { if (hash[s.charAt(left) - 'a'] >= 0) { count++; } hash[s.charAt(left) - 'a']++; left++; } } return result; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值