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;
    }
}

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;
    }
}

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;
    }
}

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;
    }
}

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;
    }
}

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没有走到头,说明没找到合适的匹配
    }

现在重点在于找到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;
    }

代码

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;
    }
}

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;
    }
}

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;
    }
}

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值