• LeetCode刷题(10)


    字符串相关问题

    242. Valid Anagram (Easy)

    问题描述
    给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

    注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

    示例 1:

    输入: s = “anagram”, t = “nagaram”
    输出: true
    示例 2:

    输入: s = “rat”, t = “car”
    输出: false

    思路
    用一个哈希表来存储每个字母出现的次数,s中如果出现则该字母数量+1,t中如果出现该字母数量-1。这样,如果哈希表中所有字母数量都为0时,返回true,否则返回false。

    代码

    class Solution {
        public boolean isAnagram(String s, String t) {
            if(s.length()!=t.length())return false;
            int[] hash = new int[26];
            for (int i = 0; i < s.length(); i++) {
                hash[s.charAt(i)-'a']++;
                hash[t.charAt(i)-'a']--;
            }
            for (int i = 0; i < 26; i++) {
                if(hash[i]!=0)return false;
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    205. Isomorphic Strings (Easy)

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

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

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

    输入输出样例

    示例 1:

    输入:s = “egg”, t = “add”
    输出:true

    示例 2:

    输入:s = “foo”, t = “bar”
    输出:false

    思路
    记录两个字符创中每一个字母第一次出现(或最后一次出现的位置),然后比较它们对应的每个字符第一次出现的位置是否相同,如果完全相同则返回true,否则为false。

    代码

    class Solution {
        public boolean isIsomorphic(String s, String t) {
            int[] preIndexOfs = new int[256]; //字符串中的字符包含字母或者数字
            int[] preIndexOft = new int[256];
            for (int i = 0; i < s.length(); i++) {
                preIndexOfs[s.charAt(i)] = i; //记录每一个字母最后一次出现的索引位置
                preIndexOft[t.charAt(i)] = i;
            }
            for (int i = 0; i < s.length(); i++){
            	//看他们对应字母最后一次出现位置是否相同
                if(preIndexOfs[s.charAt(i)]!=preIndexOft[t.charAt(i)])return false;
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    647. Palindromic Substrings (Medium)

    问题描述
    给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

    回文字符串 是正着读和倒过来读一样的字符串。

    子字符串 是字符串中的由连续字符组成的一个序列。

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

    示例 1:

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

    示例 2:

    输入:s = “aaa”
    输出:6
    解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

    思路
    从每一个字符开始,按照奇数偶数两种可能向外扩张(两个指针指向相同的数,向外移动),每次向外扩张一次,回文字符串的数量加1。

    代码

    class Solution {
        public int countSubstrings(String s) {
            int count=0;
            for(int i=0;i<s.length();i++){
                count+=help(s,i,i); //奇数回文
                count+=help(s,i,i+1); //偶数回文
            }
            return count;
        }
    
        public int help(String s,int l,int r){
            int count = 0;
            while (l>=0 && r<s.length() && s.charAt(l)==s.charAt(r)){
                count++;
                l--;
                r++;
            }
            return count;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    696. Count Binary Substrings (Easy)

    问题描述
    给定一个字符串 s,统计并返回具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是成组连续的。

    重复出现(不同位置)的子串也要统计它们出现的次数。

    输入输出样例

    示例 1:

    输入:s = “00110011”
    输出:6
    解释:6 个子串满足具有相同数量的连续 1 和 0 :“0011”、“01”、“1100”、“10”、“0011” 和 “01” 。
    注意,一些重复出现的子串(不同位置)要统计它们出现的次数。
    另外,“00110011” 不是有效的子串,因为所有的 0(还有 1 )没有组合在一起。

    示例 2:

    输入:s = “10101”
    输出:4
    解释:有 4 个子串:“10”、“01”、“10”、“01” ,具有相同数量的连续 1 和 0 。

    思路
    观察字符串可以发现该规律:上一组相同字符串个数pre,大于等于当前相同字符串的个数current,即可有一组满足要求的子串,统计满足条件的字串数量即可。

    代码

    class Solution {
        public int countBinarySubstrings(String s) {
            int count=0,current=1,pre=0;
            for (int i = 1; i < s.length(); i++) {
                if(s.charAt(i)==s.charAt(i-1)){
                    current++;
                }else {
                    pre = current;//使用pre保存上一组相同字符的数量
                    current = 1;//重新将当前改组字符串的数量初始化为1
                }
                if(pre>=current)count++;
            }
            return count;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    227. Basic Calculator II (Medium)

    问题描述
    给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

    整数除法仅保留整数部分。

    你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。

    注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

    输入输出样例

    示例 1:

    输入:s = “3+2*2”
    输出:7
    示例 2:

    输入:s = " 3/2 "
    输出:1

    思路
    就是一个没有特殊运算和括号的简易计算器。可以简化将符号栈简化为一个变量用于存储上一个运算符。用一个数字栈存储数字。

    如果字符是数字,就将这些数字转换为int类型的值num。
    对上一个运算符进行判断:
    如果是“+”,直接将num放入数字栈中;
    如果是“-”,将-num放入栈中;
    如果是“*”,将栈顶元素弹出stack.pop()*num放入栈中;
    如果是“/”,将栈顶元素弹出stack.pop()/num放入栈中;

    最后将数字栈中的内容相加即为最终的结果。

    代码

    class Solution {
        public int calculate(String s) {
            char preSign = '+'; //由于第一个正数字前面的正号一般都省略,可以初始化为+,后面的正数可以根据其前运算符直接放入栈中
            int num=0; //保存连续的数字字符组成的数字
            Deque<Integer> stack = new LinkedList<>();
            for (int i = 0; i < s.length(); i++) {
                char c=s.charAt(i);
                if(c<='9' && c>='0'){
                    num=num*10+(c-'0');
                }
                if((c=='+'||c == '-'||c == '*'||c == '/' )|| i==s.length()-1){//连续数字结束之后,要对num进行相应处理,同时更新preSign
                    if(preSign == '+'){
                        stack.offerLast(num);
                    }
                    else if(preSign == '-'){
                        stack.offerLast(-num);
                    }
                    else if(preSign == '*'){
                        stack.offerLast(stack.pollLast()*num);
                    }else if(preSign == '/'){
                        stack.offerLast(stack.pollLast()/num);
                    }
                    preSign = c;
                    num=0;
                }
            }
            int sum=0;
            while (!stack.isEmpty()){
                sum+=stack.pollLast();
            }
            return sum;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    28. Implement strStr() (Easy)

    问题描述

    实现 strStr() 函数。
    给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
    说明:
    当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
    对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

    输入输出样例

    示例 1:

    输入:haystack = “hello”, needle = “ll”
    输出:2
    示例 2:

    输入:haystack = “aaaaa”, needle = “bba”
    输出:-1

    思路
    暴力解法很容易想到,本题最重要的是考察KMP算法。
    KMP算法具体的过程可以参考:https://www.zhihu.com/question/21923021/answer/281346746

    大致思路如下:

    使用一个i指针指向待搜索字符串的首字符,j指向模板字符串的首字符。
    如果使用暴力解法,则每次i指针向后移动到不匹配的字符后都需要回到原来的位置(的下一个位置),j回到模板字符的首字符重新开始遍历匹配比较。
    KMP算法的优化在于,i指针每次比较向后移动一位且之后不再向前移动,j指针每次回溯到next[j]的位置,再向后移动比较。

    代码大致为:

    public int strStr(String haystack, String needle) {
            int i=0,j=0;
            int m = haystack.length();
            int n = needle.length();
            int[] next = getNext(needle);
            while (j<n && i<m){
                if(j==-1 || haystack.charAt(i)==needle.charAt(j)){
                    i++; //i只向前移动
                    j++;
                }else j=next[j]; //j回溯到next[j]的位置
            }
            if(j==needle.length()){//如果j到了模板的最后面,说明完全匹配上了
                return i-j; //匹配的初始位置=i当前位置-模板的长度
            }
            return -1;//i走到头,j没有走到头,说明没找到合适的匹配
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    现在重点在于找到next数组。
    next数组i值的意义在于:遍历到模板i字符时,i前面的字符串前缀集合与后缀集合的交集中最长元素的长度。例如abcabc的前缀集合与后缀集合的交集中最长元素的长度为3。
    求解next的过程,相当于使用一个模板字串p1(i从1开始)匹配另外一个相同模板字符串p2(j从0开始)的过程。如果对应的字符相同,则更新next值,如果不相同,j需要回溯到next[j]的位置。
    代码为:

    public int[] getNext(String pString){
            int m = pString.length();
            int[] next = new int[m+1];
            next[0]=-1; //为了方便编程,将next数组初始位置置为-1
            int i=0,j=-1;//求next过程类似以自身为模板进行匹配,i指向待匹配字符串,j为模板字符串
            while (i<m){
                if(j==-1 || pString.charAt(i)==pString.charAt(j)){
                    i++;
                    j++;
                    next[i]=j; //如果匹配,更新当前字符对应的next值
                }else {
                    j = next[j];//如果不匹配,j向前回溯(回溯到当前next中记录的位置)
                }
            }
            return next;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    代码

    class Solution {
        public int strStr(String haystack, String needle) {
            int i=0,j=0;
            int m = haystack.length();
            int n = needle.length();
            int[] next = getNext(needle);
            while (j<n && i<m){
                if(j==-1 || haystack.charAt(i)==needle.charAt(j)){
                    i++;
                    j++;
                }else j=next[j];
            }
            if(j==needle.length()){
                return i-j;
            }
            return -1;
        }
    
        //next数组中的i值是i前面的字符串的前缀集合与后缀集合的交集中最长元素的长度
        public int[] getNext(String pString){
            int m = pString.length();
            int[] next = new int[m+1];
            next[0]=-1; //为了方便编程,将next数组初始位置置为-1
            int i=0,j=-1;//求next过程类似以自身为模板进行匹配,i指向待匹配字符串,j为模板字符串
            while (i<m){
                if(j==-1 || pString.charAt(i)==pString.charAt(j)){
                    i++;
                    j++;
                    next[i]=j; //如果匹配,更新当前字符对应的next值
                }else {
                    j = next[j];//如果不匹配,j向前回溯(回溯到当前next中记录的位置)
                }
            }
            return next;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    409. Longest Palindrome (Easy)

    问题描述
    给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的回文串 。

    在构造过程中,请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。

    输入输出样例
    示例 1:

    输入:s = “abccccdd”
    输出:7
    解释:
    我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。

    示例 2:

    输入:s = “a”
    输入:1

    思路
    使用一个哈希表统计每个字符出现的次数,如果出现偶数次,它就可以直接增加回文子序列的长度,如果是奇数次,它可以贡献(count/2)*2的长度。最后要最长的子回文,有奇数个的字符可以放在中间从而使自序列长度再加1。

    代码

    class Solution {
        public int longestPalindrome(String s) {
            Map<Character,Integer> map = new HashMap<>();
            for(char c:s.toCharArray()){
                map.put(c,map.getOrDefault(c,0)+1);
            }
            Set<Character> sets = map.keySet();
            int count=0; //保存回文一半的长度
            int num=0; //用于判断有没有奇数个的字符
            for(char set:sets){
                count+=map.get(set)/2;
                if(map.get(set)%2==1){
                    num = 1;
                }
            }
            return count*2+num;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3. Longest Substring Without Repeating Characters (Medium)

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

    输入输出样例
    示例 1:

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

    示例 2:

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

    思路
    首先使用一个Hashmap存放每个字符出现的位置。使用一个i指针和一个j指针,j指针一直往前移动,当遇到map中存在的字符时,统计长度(j-i),i指针移动到存在的字符位置的下一个位置并将哈希表中i指针之前的字符删除。

    最后还要考虑如果没有出现重复元素,则j移动到了串尾,无重复的字符长度为j-i。

    代码

    class Solution {
        public int lengthOfLongestSubstring(String s) {
            Map<Character,Integer> position = new HashMap<>();
            int i=0,j=0;
            int longest = 0;
            while (i<s.length() && j<s.length()){
                if(position.containsKey(s.charAt(j))){
                    longest = Math.max(longest,j-i);
                    int k=position.get(s.charAt(j));//重复元素所在的位置k
                    int t=i; //记录i的位置
                    i = k+1; //i移动到重复元素的下一个位置
                    while (t<=k){ //从哈希表中删除从i原位置到k之间的元素
                        position.remove(s.charAt(t));
                        t++;
                    }
                }
                position.put(s.charAt(j),j);
                j++;
            }
            longest = Math.max(longest,j-i);
            return longest;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    5. Longest Palindromic Substring (Medium)

    问题描述
    给你一个字符串 s,找到 s 中最长的回文子串。

    输入输出样例

    示例 1:

    输入:s = “babad”
    输出:“bab”
    解释:“aba” 同样是符合题意的答案。

    示例 2:

    输入:s = “cbbd”
    输出:“bb”

    思路
    和前面的 647. Palindromic Substrings (Medium) 有些类似,分别从偶数和奇数情况遍历字符串,然后用两个指针向外扩张。最后统计这些回文字符串中最长的一个。

    代码

    class Solution {
        public String longestPalindrome(String s) {
            String result="";
            for (int i = 0; i < s.length(); i++) {
                String a = Palindrome(s,i,i);
                String b = Palindrome(s,i,i+1);
                if(a.length()>b.length()){
                    if(a.length()>result.length()){
                        result = a;
                    }
                }else {
                    if(b.length()>result.length()){
                        result = b;
                    }
                }
            }
            return result;
        }
    
        public String Palindrome(String s,int l,int r){
            while (l>=0 && r<s.length() && s.charAt(l)==s.charAt(r)){
                l--;
                r++;
            }
            return s.substring(l+1,r);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    大白话解释什么类加载机制
    刷脸支付对旅游行业的影响
    CRM系统中联系人管理的十大好处
    OpenHarmony中SystemAbility的实现方法
    SpringMVC的执行流程
    Python 模拟类属性
    MAC: 使用技巧
    Kafka 数据重复怎么办?(案例)
    讯飞输入法怎么用密语模式
    如何将vue-element-admin中的excel导入导出功能模块, 单独拿出来用 更新中...
  • 原文地址:https://blog.csdn.net/ha_lee/article/details/126353044