LeetCode字符串刷题分析与总结

2020/8/16 15:00 开始
争取刷30-40道
预计2020/8/26 完成

套路

字符串常用的方法

  • 简单的通过指针进行遍历
  • 递归+回溯的方法进行暴力的查找
  • 动态规划进行处理(求子串,子序列,回文串的时候)
  • 双指针尤其是前后指针的处理方法,有点类似于链表的双指针方法),或者滑动窗口
  • 借助数据结构:(1)借助栈对于字符串中的一些数据进行对消的操作;(2)借助hashMap统计字符串中每个元素出现的次数进行分析
  • 回文串:则通过首尾指针
  • 子串,子序列:滑动窗口,双指针或者动态规划的问题

技巧

  • 对于字符串的操作往往需要采取s.toCharry()的方式将其转换为数组,这样对于数组中元素的删除,插入都比较方便
  • 可以采用StringStringBuilder或者StringBuffer的方式进行增删盖茶

对字符串中元素的遍历/查找/判定格式

  • 字符串的遍历往往采用指针对其进行遍历
  • 和字符串次数有关采用hash,Map<Character,Integer> 其中key存储遍历过程中的字符,value存储在字符串中的每个字符出现的次数
    遍历字符串对每个字符出现的次数进行统计并进行分析

双指针 /滑动窗口

往往第一步需要将字符串转换为字符数组



滑动窗口
在这里插入图片描述



/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
    Map<char, int> need, window;
    // 将待比较的字符串中的字符以及字符出现的次数
      for(int i =0;i<p.length();i++) {
            need.put(p.charAt(i),need.getOrDefault(p.charAt(i),0)+1);
        }

    int left = 0, right = 0;
    int valid = 0; 
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s.charAt(right);
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        ...

        /*** debug 输出的位置 ***/
        printf("window: [%d, %d)\n", left, right);
        /********************/

        // 判断左侧窗口是否要收缩
        while (window needs shrink) {
            // d 是将移出窗口的字符
            char d = s.charAt(left);
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}



  • LeetCode76最小覆盖子串:(1)将need字符串遍历将其存到needMap中,并记录valid (2)我们在字符串S中使用双指针中的左右指针技巧,初始化left=right=0,把索引左闭右开区间[left,right)称为一个[窗口](3)我们先不断增加right指针扩大窗口[left,right),直到窗口中的字符串符合要求(包含了T中的所有字符)(4)此时,我们停止增加right,转而不断增加left指针缩小窗口[left,right)直到窗口中的字符串不再符合要求(不包含T中的所有字符同时,每次增加left,我们都要更新一轮结果(两个字符串
  • LeetCode28字符串的查找indexof:注意这个是子字符串的查找不是字符串序列的查找,因此窗口只能维持在need字符串的大小(两个字符串,注意这种球的是序列,不符合本文题意,主要是一种思路的借鉴
  • LeetCode567字符串的排列,给定两个字符串s1和s2,写一个函数来判断s2是否包含s1的排列:套用模板注意是字符串的排列,(连续的字符串)(连个字符串
  • LeetCode438 找到字符串中所有字母异位词
    :套用模板即可
  • LeetCode3最长无重复子串:定义欢动窗口map,[left,right)不断右移动指针,若发现有重复的元素,则发现在窗口内有重复的元素,则不断将左指针左移动(重点
  • LeetCode239滑动窗口内的最大值:滑动窗口:使用一个队列(队列中存放的是数组中元素的索引值),确保队首元素是滑动窗口内的最大值,如何保证,每添加一个元素要判断,这个元素是否大于队列中现有元素,若大于则不断移除队列中的元素,再判断队列中的元素是否不再此时滑动窗口范围内

使用栈解决问题

参考内容

使用栈的情况一般是后面的元素会对前面的元素造成影响,因此将前面的元素加入栈中,后面的元素加入的时候和前面的元素进行比对,若发现某种特性则可以将栈中的元素进行弹出

递归/回溯

参考

动态规划解决

子串,子序列的问题

cankao

一般来说。这类问题都是让你求一个最长子序列,因为最短子序列就是一个字符,没啥可问的;一旦涉及到子序列和最值,那几乎就可以肯定,考察的是动态规划技巧,时间复杂度是O(N^2)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • LeetCode516最长回文子序列:(1)定义状态:dp[i][j]在子串s[i…j]中最长回文子序列的长度;(2)状态转移方程:dp[i][j]=dp[i+1][j-1]+2(若s[i]==s[j]);否则dp[i][j]=max(dp[i+1][j],dp[i][j-1])(主要还是正确定义 dp 数组的含义,遇到子序列问题,首先想到两种动态规划思路,然后根据实际问题看看哪种思路容易找到状态转移关系,另外,找到状态转移和baseCase之后,一定要观察DP table,看看怎么遍历才能保证通过已计算出来的结果解决新的问题
  • LeetCode5最长回文子串:注意回文子串与回文子序列的区别,子串要求必须是连续的;(1)定义状态:dp[i][j]在子串s[i…j]中最长回文子串;(2)装太转移方程:dp[i][j] = s.charAt(i)+dp[i+1][j-1]+s.charAt(j);(若s[i…j]能构成回文串,注意和上面的区别)否则 dp[i][j]=dp[i][j-1],dp[i+1][j];

两个字符串/字符串的匹配

回文串

回文串的常见三种做法:双指针reverse

  • LeetCode125验证回文串:给定一个字符串,验证她是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写;采用双指针对字符串进行双向遍历,若发现当前字符串不符合要求则跳过
  • LeetCode680 验证回文串(最多删除一个字符)
    :判断回文串显然是用双指针,i从前往后遍历,j从后往前遍历,难点即是怎么去判断删除一个元素后的字符串是不是回文串,以"abdda"这个串为例,此时i指向’b’,j指向’d’,发现不对了 但是有一次删除的机会,我们自己写几个case其实就能发现,此时子串范围为(i+1, j)或(i, j-1)的俩子串只要有任意一个是回文串,则结果就是回文串,否则就不是。

字符串的翻转

  • 往往需要先对字符串转换为数组然后进行操作
  • 一般可以采用双指针对数组进行遍历
    • 若是对整个字符串进行翻转,则需要定义整个字符串的首尾指针
    • 若是对字符串中的一个个读单词,则指针需要对字符串中的一个个单词进行,(关键是如何判别单词,根据空格对字符串中的一个个单词进行区分
  • 最后将处理完的数组转换为字符串
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值