【无标题】

一.找到字符串里面所有的异位词

1.题目

初次看到此题我们一般默认对p 这个字符串里面的字符进行排列组合

"abc" "acb" ''bac'' '' bca'' '' cba'' '' cab'' 之后对 s 这个字符串进行暴力遍历,若存在则返回

**"起始"**位置的下标

其实,不妨换个思路:p这个字符串里面每一个字符出现的次数是固定的,我们只需要记录每一个

字符出现的次数,对s字符串进行遍历只要是对应字符出现的次数相同,那么就是所谓的"字母异位

词"。

借助2个哈希表:记录每一个字符出现的次数

解法一:暴力求解 + 哈希表

解法二:滑动窗口 + 哈希表

2.算法原理

1)暴力求解:定义2个"哈希表",hash1 [26] 记录p 里面每一个字符出现的次数

定义2个指针 left = 0,right = 0;

**hash2[26]**记录s 这个字符串里面每一个字符出现的次数

同时记录一下p字符串的大小:len,在s 里面每隔len 个字符进行判断一个是否相等

2)滑动窗口:我们发现每一次进行新的比较的时候,right 指针都需要再次回到left 指向的下一个

位置,其实这是没有必要 的,在[let, right]这个区间最大的字符有效个数是固定的,所有每次right

指针可以停留在"原来的位置",对left 指针进行更新:left 指针++

这不"滑动窗口"的思想就来了。

之所以把哈希表的大小设置为26,是因为题目已给出说明:2个字符串的均是由小写字母组成的。

3.分析
4·OJ代码
版本1的代码:
cpp 复制代码
    vector<int> findAnagrams(string& s, string& p)
    {
        vector<int>ret;
        int hash1[26] = {0};//记录p 里面字符出现的次数
        int hash2[26] = {0};//记录s 里面字符出现的次数

        for(auto it :p)
          hash1[it-'a']++;

        int left = 0,right = 0,len = p.size();
        int n = s.size();

        for(;right < n;right++)
        {
            //进窗口
            char in = s[right];
            hash2[in-'a']++;
            //判断 & 出窗口
            if(right-left+1 > len )
            {
                //删除left对应元素出现的次数
                char out = s[left++];
                hash2[out-'a']--;
            }
            //结果更新
            if(right-left+1 == len)
            {
                 int i = 0,j = 0;
                 int flag = 1;//标志位:默认2个哈希表一样
                //先判断2个哈希表是否一致
                while(i < 26)
                {
                    if(hash1[i++] != hash2[j++])
                    {
                        flag = 0;
                        break;
                    }
                }
                if(flag)
                ret.push_back(left);
                else
                flag = 1;//及时更新,便于下一次判断

            }

        }
         // 可能存在righ 走到n的时候,此时元素个数 [left right-1] == len 
         if(len == right-left+1 )
         {
            int i = 0,j = 0;
           int flag = 1;//标志位:默认2个哈希表一样
          while(i < 26)
            {
                if(hash1[i++] != hash2[j++])
                {
                    flag = 0;
                    break;
                }
            }
             if(flag)
               ret.push_back(left);
         }
    return ret;
    }

咱来看看运行结果咋样~~~

其实对于这个更新结果里面的判断部分咱们可以进行优化的,虽然说优化前后时间复杂度都是

O(N) 级别,但是思想是很巧妙的

**采用计数方法对count :**表示滑动窗口内有效的字符个数,注意不同的字符才算有效的字符。

分析见下:

优化后的代码:
cpp 复制代码
  vector<int> findAnagrams(string& s, string& p) 
    {
   
             int hash1[26] = { 0 };//统计p 里面每个字符出现的有效字符个数
    int hash2[26] = { 0 };
    for (auto it : p)
        hash1[it - 'a']++;

    int len = p.size();
    vector<int>ret;
    //滑动窗口
    for (int left = 0, right = 0, count = 0; right < s.size(); right++)
    {
        //进窗口
        // hash2[s[right]-'a']++;
        //进窗口的同时要维护count(滑动窗口的有效字符个数)
        // if (++hash2[s[right] - 'a'] <= hash1[s[right] - 'a'])//同一个字符可能出现不止1次
         char in = s[right];
        if (++hash2[in- 'a'] <= hash1[in - 'a'])//同一个字符可能出现不止1次
            count++;
        //出窗口 这时候只需要出一个元素即可
        //注意 不管hash2[s[left]-'a'] > hash1[p[left]-'a'] 还是<= 都需要--hash2[s[right]-'a']
        if (right - left + 1 > len)
        {
            char out = s[left++];
            if (hash2[out- 'a']-- <= hash1[out - 'a'])//条件不管是否满足left 对应 的元素都需要删除
                count--;//此时删除的是一个有效字符
        }
        //    else

        // hash1[s[left]-'a']--;//删除left 指向的元素
        //结果更新
        if (count == len)
        {
            ret.push_back(left);
        }
        }
        return ret;

    }

此时的运行效果:

二·串联所有单词的子串

1.题目

其实本题可以简化为找单词的异位词 ,只不过这里的单词的长度是固定的:words[0] .size

这里借助上面找字母异位词的思想,我们发现其实这道题并不是那么困难哈~~~

注意这里有一个关键的条件:就是words 数组里面的所有单词的大小都是固定的

记录为len = words[0].size(); 注意因为里面每一个元素都是字符串,需要借助size()函数进行求出

每一个单词的大小。

把words这个数组的大小记录为 sz = words.size():表示words 数组里面字符串的个数多少

2.算法原理

滑动窗口 + count 计数

在这直接上"滑动窗口",对于暴力求解的算法就不在一一叙述了

还是老问题,每当对[left rigth ]指定的区间进行遍历完后,right 指针没有必要再次回到left 指向的

下一个位置,对于这个区间有效单词的 个数是一定的,只需要一定left 指针所在的位置即可。

依然还是下面的几个步骤:

进窗口+维护count

出窗口+维护count

结果更新+维护count

3.分析
4·OJ代码
cpp 复制代码
    vector<int> findSubstring(string& s, vector<string>& words) 
    {
        unordered_map<string,int> hash1;//记录words里面每一个单词出现的次数
        for(auto it:words)
          hash1[it]++;
        int sz = words.size();//单词的总个数
        int len = words[0].size();//每一个单词的长度
        int n = s.size();
        vector<int>ret;

        for(int i = 0;i< len ;++i)
        {
            int count = 0;//记录滑动窗口内在words里面的单词个数
            unordered_map<string,int>hash2;//记录s里面每一个单词出现的次数

            for(int left = i,right = i; right+len <= n;right += len)//注意条件判断:必须保证进入之后走len步长是一个有效单词
            {
                //进窗口
                string in  = s.substr(right,len);//借助函数取到指定的单词
                if(++hash2[in] <= hash1[in])
                   count++;//进入的是一个有效的单词
                //出窗口
                if(right-left+1 > len*sz)
                {
                    string out = s.substr(left,len);
                    left +=len;//注意这里是移动len个步长
                    if(hash2[out]-- <= hash1[out])
                      count--;//删除的是一个有效单词
                }
                if(count == sz)
                  ret.push_back(left);

            }
        }
        return ret;


    }

运行结果见下:

三·最小覆盖子串

1.题目
2.算法原理

关于滑动窗口的OJ 题目,也是做了不少的,无非就是双指针的移动 + "4步走"

进窗口;判断;出窗口;结果更新。

这个题与之前略微还是有点不同;采用额外的两个变量 kind ,count进行记录

kind 记录:t 字符串有效的字符个数(非首次出现的字符不会进行记录)

count:记录窗口内有效的字符个数

3.分析
4·OJ代码
cpp 复制代码
string minWindow(string& s, string& t) 
    {
        //哈希表+滑动窗口
        int hash1[128] = {0};
        int hash2[128] = {0};
        //开128空间而不开52个空间:涉及到大写字母的问题,在转化为下标的时候,存在溢出或者负数的情况
        int kind = 0;//记录t里面有效(非首次出现的字符就不在进记录)字符的个数
        for(auto it: t)
            if(hash1[it]++ == 0)//只要是首次出现,kind就++
                kind++;
        int count = 0;//记录窗口内有效的字符个数
        int start = -1;//记录子串的起始位置
        int sz = INT_MAX;
        for(int left = 0,right = 0;right < s.size();++right)
        {
            //进窗口
            char in = s[right];
            if(++hash2[in] == hash1[in])
                count++;//有效字符
            //出窗口
            while(count == kind )
            {
                if(right-left+1 < sz)
                 {
                    sz = right-left+1;//最小区间的更新
                    start = left;//位置的更新
                 }

                    //元素删除
                char out = s[left++];
                if(hash2[out]-- == hash1[out])
                    count--;//删除有效字符
            }

        }
        return start == -1 ? string():s.substr(start,sz);    

    }

手搓了这么久的代码,来看看运行结果咋样吧~~~

相关推荐
枫の准大一5 分钟前
C++从零到满绩——类和对象(中)
开发语言·c++
HEX9CF11 分钟前
【数字图像处理+MATLAB】通过 Roberts, Prewitt, Sobel, LoG 等算子实现图像边缘检测:使用 edge 函数
开发语言·matlab·edge
9毫米的幻想12 分钟前
【Linux系统】—— 基本指令(三)
linux·c语言·c++·学习
凡人的AI工具箱22 分钟前
40分钟学 Go 语言高并发实战:高性能缓存组件开发
开发语言·后端·缓存·架构·golang
大白的编程日记.25 分钟前
【C++笔记】数据结构进阶之二叉搜索树(BSTree)
开发语言·数据结构·c++·笔记
心.c26 分钟前
0-1背包问题
c++·算法
每天一个秃顶小技巧27 分钟前
01.Golang 源码目录结构
开发语言·后端·golang
小柯J桑_28 分钟前
C++:用红黑树封装map与set-1
开发语言·c++·set·map·红黑树
kitesxian30 分钟前
Leetcode146. LRU 缓存(HOT100)
数据结构·算法·leetcode·缓存
2401_8532757333 分钟前
Java IO 基础知识总结下
java·开发语言