leetcode题解整理——字符串

327 阅读3分钟

242 有效的字母异位词

题目描述:

给定两个字符串s和t,编写一个函数来判断t是不是s的字母异位词。也就是判断两个字符串包含的字符是否完全相同。

示例:

输入: s = "anagram", t = "nagaram"
输出: true
输入: s = "rat", t = "car"
输出: false

说明: 你可以假设字符串只包含小写字母。

题解:

  • 首先判断两个字符串长度是否相等,不相等则直接返回 false;
  • 然后我们new一个HashMap<Character,Integer>,用来存储字符及对应的次数;
  • 将字符串s放入map中,同时记录字符出现次数cnt;
  • 遍历字符串t中的字符,若map无,直接返回false;
  • 若有,先判断对应cnt是否大于0,是,则对应cnt-1,否,则直接返回false;
  • 如果cnt<0,就返回false;
  • 结束。

代码:

import java.util.HashMap;
import java.util.Map;

/*
 * @lc app=leetcode.cn id=242 lang=java
 *
 * [242] 有效的字母异位词
 */

// @lc code=start
class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        //new hashmap
        Map<Character,Integer> sMap = new HashMap<>();
	   // 遍历s字符串
        for (char c : s.toCharArray()) {
            sMap.put(c, sMap.getOrDefault(c, 0) + 1);
        } 
        /*
         for (char c : t.toCharArray()) {
             sMap.put(c, sMap.getOrDefault(c, 0) - 1);
             if (sMap.get(c) < 0) {
                 return false;
             }
        }*/
	   // 遍历t字符串
        for (char c : t.toCharArray()) {
            if (sMap.containsKey(c) && sMap.get(c) > 0) {
                sMap.put(c, sMap.get(c) - 1);
            }else{
                return false;
            }
        }
        return true;
    }
}
// @lc code=end

205同构字符串

题目描述:

给定两个字符串 s 和t,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

示例:

输入:s = "egg", t = "add"
输出:true
输入:s = "foo", t = "bar"
输出:false
输入:s = "paper", t = "title"
输出:true

可以假设s和 t长度相同。

题解:

我们判断 s和 t 每个位置上的字符是否都一一对应,即 s 的任意一个字符被 t 中唯一的字符对应,同时 t 的任意一个字符被 s 中唯一的字符对应。这也被称为「双射」的关系。

以示例 s = "foo", t = "bar"为例,t 中的字符 a 和 r 虽然有唯一的映射 o,但对于 s 中的字符 o 来说其存在两个映射 {a,r},故不满足条件。

因此,我们维护两张哈希表,第一张哈希表 s2t 以 s 中字符为键,映射至 t 的字符为值,第二张哈希表 t2s 以 t 中字符为键,映射至 s 的字符为值。从左至右遍历两个字符串的字符,不断更新两张哈希表,如果出现冲突(即当前下标 index 对应的字符 s[index] 已经存在映射且不为 t[index], 或当前下标 index 对应的字符 t[index] 已经存在映射且不为s[index])时说明两个字符串无法构成同构,返回 false。

如果遍历结束没有出现冲突,则表明两个字符串是同构的,返回 true 即可。

代码:

import java.util.HashMap;
import java.util.Map;

/*
 * @lc app=leetcode.cn id=205 lang=java
 *
 * [205] 同构字符串
 */

// @lc code=start
class Solution {
    public boolean isIsomorphic(String s, String t) {
        //定义两个哈希表
        Map<Character,Character> s2t = new HashMap<>();
        Map<Character,Character> t2s = new HashMap<>();

        int size = s.length();
        //遍历字符串
        for (int i = 0; i < size; i++) {
            char a = s.charAt(i);//s中字符
            char b = t.charAt(i);//t中字符
            //当前下标 i 对应的字符 s[i] 已经存在映射且不为 t[i]
            if (s2t.containsKey(a) && s2t.get(a) != b) {
                return false;
            }
            //当前下标 i 对应的字符 t[i] 已经存在映射且不为s[i]
            if (t2s.containsKey(b) && t2s.get(b) !=a ) {
                return false;
            }
            s2t.put(a, b);
            t2s.put(b, a);
        }
        
        return true;
    }
}
// @lc code=end

647回文子串

题目描述:

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

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

示例:

输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

题解:

把字符串中的 一个或者相邻两个字符 当作中心,然后通过两个指针 分别向左向右 扩展,并在扩展的过程中记录当前时刻是否具有 回文属性。

代码:

/*
 * @lc app=leetcode.cn id=647 lang=java
 *
 * [647] 回文子串
 */

// @lc code=start
class Solution {
    public int countSubstrings(String s) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            count += extendSubstrings(s, i, i);//偶数
            count += extendSubstrings(s, i, i+1);//奇数
        }
        return count;
    }
    public static int extendSubstrings(String s,int left,int right){
        int count = 0;
        while (left >= 0 && right <s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
            ++count;
        }
        return count;
    }
}
// @lc code=end

696计数二进制子串

题目描述:

给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。

重复出现的子串要计算它们出现的次数。

示例:

输入: "00110011"
输出: 6
解释: 有6个子串具有相同数量的连续10: “0011”,“01”,“1100”,“10”,“0011” 和 “01”。

请注意,一些重复出现的子串要计算它们出现的次数。

另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

输入: "10101"
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续10

题解:

  1. 官方题解:

    我们可以将字符串 s 按照 0 和 1 的连续段分组,存在counts 数组中,例如 s=00111011,可以得到这样的counts 数组:counts={2,3,1,2}。

    这里counts 数组中两个相邻的数一定代表的是两种不同的字符。假设 counts 数组中两个相邻的数字为 u 或者 v,它们对应着 u 个 0 和 v 个 1,或者 u 个 1 和 v 个 0。它们能组成的满足条件的子串数目为 min{u,v},即一对相邻的数字对答案的贡献。

    我们只要遍历所有相邻的数对,求它们的贡献总和,即可得到答案。

  2. 类似回文子串解法:

代码:

  1. 官方解答代码:

    class Solution {
        public int countBinarySubstrings(String s) {
            List<Integer> counts = new ArrayList<Integer>();
            int ptr = 0, n = s.length();
            while (ptr < n) {
                char c = s.charAt(ptr);
                int count = 0;
                while (ptr < n && s.charAt(ptr) == c) {
                    ++ptr;
                    ++count;
                }
                counts.add(count);
            }
            int ans = 0;
            for (int i = 1; i < counts.size(); ++i) {
                ans += Math.min(counts.get(i), counts.get(i - 1));
            }
            return ans;
        }
    }
    
  2. 类似回文解法

    class Solution {
        public int countBinarySubstrings(String s) {
            int result = 0;
            char[] chars = s.toCharArray();
            for(int i = 1; i < s.length(); i++){
                int left = i - 1, right = i;
                char leftChar = chars[left];
                char rightChar = chars[right];
                if(leftChar == rightChar)
                    continue;
                while(left >= 0 && right < s.length() && chars[left] == leftChar && chars[right] == rightChar){
                    left--;
                    right++;
                    result++;
                }
            }
            return result;
        }
    }