20220123剑指offer刷题

剑指 Offer II 014. 字符串中的变位词

题目描述:

给定两个字符串 s1s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。

换句话说,第一个字符串的排列之一是第二个字符串的 子串

我的想法是使用滑动窗口:

先用一个字典存放s1中的字符和字符个数,然后用滑动窗口来检验s2中是否有s1的变式。

代码如下:

public class Solution {
    public bool CheckInclusion(string s1, string s2) {
        Dictionary<char,int> S1 = new Dictionary<char,int>();
        int Window = s1.Length;
        int flag = 4;
        if(Window>s2.Length) return false;
        //用字典存储S1中的字符和字符个数
        for(int i = 0;i<s1.Length;i++){
            if(!S1.ContainsKey(s1[i])) S1.Add(s1[i],1);
            else S1[s1[i]]++;
        }
        //存储S2中的字符和字符个数
        for(int j = 0;j<s2.Length-Window+1;j++){
            Dictionary<char,int> S2 = new Dictionary<char,int>();
            for(int m = 0;m<Window;m++){
                if(!S2.ContainsKey(s2[j+m]))  S2.Add(s2[j+m],1);
                else S2[s2[j+m]]++;
            }
            foreach(var item in S1.Keys){
                if(!S2.ContainsKey(item)){
                    flag = 1;
                    break;}
                else if(S1[item]!=S2[item]){
                    flag = 2;
                    break;}
                else if(S1[item]==S2[item]) flag = 0;
            }
            if(flag==0) break;  
        }
        return flag==0?true:false;
    }
}

可以说是好不容易终于通过了,但是通过的效果却不是很好

才PK掉了8%的人🤔 不过的确我这个滑动窗口就是老老实实地一步一步地走,效率的确不高,但是要怎么才能效率更高呢?

看了一下解析,可以优化的地方是不要每次都整个字典地遍历,因为每次都只向右移动一个数,所以相当于是减少了一个数,再增加一个数,在检查的时候只需要检查这两个数的情况就可以了,这就大大减少了很多重复的计算量

此外还可以引入双指针的思想,因为上面的想法是固定长度,看s2固定窗口中的数和s1的数之间的关系,即保证区间长度为 n的情况下,去考察是否存在一个区间使得 s1中字符和字符数组和固定窗口中的一致。反过来,还可以在保证 s1中字符和字符数组和s2子串中的一致的情况下,去考察是否存在一个区间,其长度恰好为 n。

代码如下:

public class Solution {
    public bool CheckInclusion(string s1, string s2) {
        Dictionary<char, int> need = new Dictionary<char, int>();
        Dictionary<char, int> window = new Dictionary<char, int>();

        foreach (char c in s1)
        {
            if (need.ContainsKey(c)) need[c]++;
            else need.Add(c, 1);
        }

        int left = 0;
        int right = 0;
        int valid = 0;

        while (right < s2.Length)
        {
            // c 是将移入窗口的字符		
        char c = s2[right];
        // 右移窗口
        right++;
        if (need.ContainsKey(c))
        {
                if (window.ContainsKey(c)) window[c]++;
                else window.Add(c, 1);

                if (window[c] == need[c]) valid++;
        }

            // 判断左侧窗口是否要收缩
            while (right - left >= s1.Length)
            {
                // 在这里判断是否找到了合法的子串
                if (valid == need.Count) return true; //字典.Count是字典元素计数

                char d = s2[left];
                left++;
                // 进行窗口内数据的一系列更新
                if (need.ContainsKey(d))
                {
                    if (window[d] == need[d])
                        valid--;
                    window[d]--;
                }
            }
        }
        return false;
    }
}

这种思想的效率有明显的提高

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值