字符串——算法专项刷题(三)

三、字符串

字符串题目常用算法及思想: 滑动窗口、数组统计字母个数

注意点:字符串边界、窗口的维持

3.1字符串中得变位词

原题链接

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

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

示例 1:

  • 输入: s1 = "ab" s2 = "eidbaooo"
  • 输出: True
  • 解释: s2 包含 s1 的排列之一 (“ba”).

提示:

  • 1 <= s1.length, s2.length <= 104
  • s1s2 仅包含小写字母

思路: 只要保证长度相等,出现的字符及个数相同,就符合条件。因为只包含26个小写字母,因此使用长度为26的数组记录出现的字符及个数,使用窗口保证长度相等,每遍历一个元素,移动窗口更新字符个数

注意点: 先统计s1字符串长度的字符,然后从这个长度开始遍历

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        //记录 字母出现的次数
        int[] cnt1 = new int[26];
        int[] cnt2 = new int[26];
        int n = s1.length();
        int m = s2.length();
        if (n > m) return false;

        for (int i = 0; i < n; i++) {
            ++cnt1[s1.charAt(i) - 'a'];
            ++cnt2[s2.charAt(i) - 'a'];
        }
        
        if(Arrays.equals(cnt1,cnt2)) return true;

        for (int i = n; i < m; i++) {
            // 保证长度相等,每遍历一个字符左边 -1 右边+1  
      
            ++cnt2[s2.charAt(i) - 'a'];
            --cnt2[s2.charAt(i-n) - 'a'];
            //比较数组是否相等
            if(Arrays.equals(cnt1,cnt2)) return true;

        }
        return false;


    }
}

3.2字符串中的所有变位词

原题链接

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

变位词 指字母相同,但排列不同的字符串。

示例 1:

  • 输入: s = “cbaebabacd”, p = “abc”

  • 输出: [0,6]

解释:

  • 起始索引等于 0 的子串是 “cba”, 它是 “abc” 的变位词。
  • 起始索引等于 6 的子串是 “bac”, 它是 “abc” 的变位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • sp 仅包含小写字母

思路: 变位词 就是字符相同个数相等的两个字符串,与上一题思路相同,由于字符串s和p都仅包含小写字母,因此使用长度为26的数组记录字符出现的个数,然后通过比较数组是否各元素相等来判断是否符合条件

注意点: 先分别统计字符串p长度个字符,然后进行遍历,保证两个数组中记录的数据所对应的字符串的长度相等

class Solution {
    public List<Integer> findAnagrams(String s, String p) {

     List<Integer> list = new ArrayList();
      int len1 = s.length();
      int len2 = p.length();
  // 特判 
      if(len2 > len1)  return list;

      int[] num1 = new int[26];
      int[] num2 = new int[26];

      for(int i = 0; i < len2;i++){
           num1[p.charAt(i) - 'a']++;
           num2[s.charAt(i) - 'a']++;
       }
        
        //通过Arrays中的equals方法来比较两个数组中的数据是否相等
       if(Arrays.equals(num1,num2)){
           list.add(0);
       }

    for(int i = len2; i < len1 ;i++){
          num2[s.charAt(i) - 'a']++;
          num2[s.charAt(i - len2) - 'a']--;

          if(Arrays.equals(num1,num2)) list.add(i - len2 + 1);
    }

    return list;

    }
}

3.3最多删除一个字符得到回文

原题链接

给定一个非空字符串 s,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。

示例 1:

  • 输入: s = “aba”
  • 输出: true

提示:

  • 1 <= s.length <= 105
  • s 由小写英文字母组成

思路: 双指针,如果遇到左右不等,那么判断左边删除一个字符,或者右边删除一个字符,继续判断是否是回文

class Solution {
    public boolean validPalindrome(String s) {
     int l = 0;
    int  r = s.length() -1;
    while (l < r){
        char c1 = s.charAt(l), c2 = s.charAt(r);
        if(c1 == c2){

            l++;
            r--;

        } else{
        
        //左边删一个字符 或者右边删一个字符
           return valid(s,l+1,r) || valid(s,l,r-1);
        }

    }

    return true;


    }

     boolean valid(String s, int l, int r) {
        while(l < r){
            if(s.charAt(l) != s.charAt(r)) return false;
            l++;
            r--;
        }
        return true;
        
    }
}

3.4有效的回文

给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。

本题中,将空字符串定义为有效的 回文串

示例 1:

  • 输入: s = “A man, a plan, a canal: Panama”

  • 输出: true

  • 解释:“amanaplanacanalpanama” 是回文串

提示:

  • 1 <= s.length <= 2 * 105
  • 字符串 s 由 ASCII 字符组成

思路: 双指针判断是否是回文,跳过不是字母和数字的字符

注意点: 在大小写转化时,注意类型转换为char,而不是ASCII码

class Solution {
    public boolean isPalindrome(String s) {
        int l = 0, r = s.length() - 1; 
        while (l < r) {
            while (l < r && !check(s.charAt(l))) l++;
            while (l < r && !check(s.charAt(r))) r--;
            if (l < r && !isEquals(s.charAt(l), s.charAt(r))) return false;
            l++;
            r--;
        }
        return true;
    }
    public boolean check(char c) { //检测字符c是否为数字或字母
        return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9';
    }
    public boolean isEquals(char a, char b) { //忽略大小写比较两个字符
        if ('A' <= a && a <= 'Z') a = (char)(a + 'a' - 'A');
        if ('A' <= b && b <= 'Z') b = (char)(b + 'a' - 'A');
        return a == b;
    }
}


3.5 不含重复字符的最长子字符串

原题链接

给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。

示例 1:

  • 输入: s = “abcabcbb”
  • 输出: 3
  • 解释: 因为无重复字符的最长子字符串是 “abc”,所以其长度为 3。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

思路:滑动窗口 + set 去重

class Solution {
    public int lengthOfLongestSubstring(String s) {

        int len = s.length();

        int l = 0, r = 0;
        int max = 0;

        // 用于 处理去重
        Set<Character> set = new HashSet<>();


        //滑动窗口
        for (int left = 0, right = 0;  right < len; right++) {

            // 如果窗口之间存在 重复字符 就 一直移动左指针
            while(set.contains(s.charAt(right))){
                set.remove(s.charAt(left));
                left++;
            }
            set.add(s.charAt(right));

            max = Math.max(max, right - left + 1);



        }

        return max;

    }
}

3.6回文子字符串的个数

原题链接

给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

  • 输入:s = “abc”
  • 输出:3
  • 解释:三个回文子串: “a”, “b”, “c”

提示:

  • 1 <= s.length <= 1000
  • s 由小写英文字母组成

解法一:

思路: 中心扩散 遍历字符串,取每一个字符作为一个回文子串的中心,按照回文串的要求向两边扩散,统计回文串的个数。

注意点: 如果字符串是偶数那么回文子串中心有两个字符,解决方案在遍历的时候,取每一个字符的同时再取该字符与下一个字符也作为中心,向两边扩散

class Solution {
    char[] ch;
    int len;
    public int countSubstrings(String s) {
        
      ch = s.toCharArray();
      len = s.length();
      int ans = 0;

      for(int i = 0; i < len ;i++){
          ans += count(i,i);
          ans += count(i,i+1);
      }

           return ans;
    }

    int count(int i, int j){
        int ans = 0;
        while(i >=0 && j < len){
            if(ch[i] == ch[j]){
                ans++;
                i--;
                j++;
            }else{
                break;
            }
        }

        return ans;
    }
}


解法二:

思路: 动态规划 dp[i][j]表示s的第i个字符到第j个字符能否组成回文串

当 s[i] 与 s[j] 不相同时 dp[i][j] 为false

当 s[i] 与 s[j] 相同时 如果下标 i 与 j 相同 那么 同一个字符符合条件,如果i 与 j 相差 1 那么由于前提条件s[i] 与 s[j] 相同 也符合条件 比如 aa

如果i 与 j 相差大于1 比如 abcda 那么判断i 到 j 区间是否是回文子串,就看 i+1 到 j- 1 是否是回文子串了 也就是 dp[i+1][j-1]

注意点:i 从后向前遍历 是因为 j在 i 右边 且状态必须是 有小到大 大的状态也必须包含小的状态(j 必须遍历 i遍历过的)

class Solution {
    public int countSubstrings(String s) {
   int  len = s.length();
   boolean[][] dp = new boolean[len][len];
   int res = 0;

   for(int i =len -1; i >=0 ; i--){
       for(int j = i; j < len;j++){
           if(s.charAt(i) == s.charAt(j) && (j- i <=1 || dp[i+1][j-1])){
               dp[i][j] = true;
               res++;
           }
       }
   }

   return res;
    }
}

时间复杂度 O (n^2)

3.7含有所有字符的最短字符串

原题链接

给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。

如果 s 中存在多个符合条件的子字符串,返回任意一个。

注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

示例 1:

  • 输入:s = “ADOBECODEBANC”, t = “ABC”

  • 输出:“BANC”

  • 解释:最短子字符串 “BANC” 包含了字符串 t 的所有字符 ‘A’、‘B’、‘C’

提示:

  • 1 <= s.length, t.length <= 105
  • st 由英文字母组成

思路: 滑动窗口 使用两个 长度为56的数组统计字符出现的个数,使用count来记录窗口中s字符串包含 t字符串中字符的个数(有效字符)

class Solution {
    public String minWindow(String s, String t) {
         int[] num1 = new int[58];
         int[] num2 = new int[58];

     int len1 = s.length();
     int len2 = t.length();

         for(int i = 0;i < len2; i++){
                 num2[t.charAt(i) - 'A']++;
         }

// count 记录有效字符
         int left = 0, right = 0, count = 0;

         String res  = "";
         while(right < len1){

           num1[s.charAt(right) - 'A']++;
           //有效字符
       if(num1[s.charAt(right) - 'A' ] <= num2[s.charAt(right) -'A']) count++;

       //如果 窗口中 s的某个字符 大于 t中 的 右移左窗口
       while(left <= right && num1[s.charAt(left) - 'A'] > num2[s.charAt(left) - 'A']){
           num1[s.charAt(left) - 'A']--;
           left++;
       }
if(count == len2 && (res.isEmpty() || res.length() > right - left + 1)) {
    res = s.substring(left,right+1);
}

           right++;

         }

         return res;

    }
}

时间复杂度 O(n)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Levenshtein Distance算法是一种常见的字符串相似度算法,也被称为编辑距离算法。其主要思想是通过计算两个字符串之间的编辑距离来确定它们的相似程度。 编辑距离指的是将一个字符串转换成另一个字符串所需的最少操作次数,其中每次操作可以是插入、删除或替换一个字符。例如,将字符串“kitten”转换成字符串“sitting”需要进行3次操作,即将“k”替换为“s”,将“e”替换为“i”,将“n”替换为“g”。 Levenshtein Distance算法的实现一般使用动态规划的方法,通过填充一个二维矩阵来计算两个字符串之间的编辑距离。具体实现过程可以参考以下伪代码: ``` function LevenshteinDistance(s1, s2): m = length(s1) n = length(s2) d = new matrix(m+1, n+1) for i from 0 to m: d[i, 0] = i for j from 0 to n: d[0, j] = j for j from 1 to n: for i from 1 to m: if s1[i] == s2[j]: cost = 0 else: cost = 1 d[i, j] = min(d[i-1, j]+1, d[i, j-1]+1, d[i-1, j-1]+cost) return d[m, n] ``` 在以上代码中,变量s1和s2分别表示两个待比较的字符串,m和n分别表示它们的长度,矩阵d用于存储编辑距离的计算结果。首先,将矩阵d的第一行和第一列分别初始化为0到n和0到m的整数。然后,对于每个(i, j)位置,如果s1[i]等于s2[j],则将cost设为0,否则设为1。最后,根据递推公式d[i, j] = min(d[i-1, j]+1, d[i, j-1]+1, d[i-1, j-1]+cost)来填充矩阵d,并返回d[m, n]作为编辑距离的结果。 Levenshtein Distance算法的时间复杂度为O(m*n),其中m和n分别为两个字符串的长度。在实际应用中,该算法可用于拼写检查、数据去重等场景。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

〖雪月清〗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值