算法通关村第十二关——字符串经典基础面试题(白银)

1 反转的问题

1.1 反转字符串

leetcode 344. 反转字符串

这道题没什么好说的

class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        while(left < right){
            char temp = s[left];
            s[left++] = s[right];
            s[right--] = temp;
        }
    }
}

1.2 反转字符串 II

leetcode 541. 反转字符串 II

这道题比较烦的就是逻辑处理这块:

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
class Solution {
    public String reverseStr(String s, int k) {
        int n = s.length();
        char[] arr = s.toCharArray();
        for (int i = 0; i < n; i += 2 * k) {
            reverse(arr, i, Math.min(i + k, n) - 1);
        }
        return new String(arr);
    }

    public void reverse(char[] arr, int left, int right) {
        while (left < right) {
            char temp = arr[left];
            arr[left++] = arr[right];
            arr[right--] = temp;
        }
    }
}

i 表示当前要进行反转的子字符串的起始位置。由于每次要反转的子字符串包括前 k 个字符,因此起始位置为 i。

Math.min(i + k, n) - 1 用来确定子字符串的结束位置。

  • 其中,i + k 表示子字符串的最大可能结束位置,n 是原始字符串的长度。

  • 如果 i + k 的值小于 n,则说明当前子字符串的长度不足 k,可以直接取 i + k 作为结束位置。

  • 否则,即 i + k >= n,说明当前子字符串的长度超过等于 k,应该只取到 n - 1 作为结束位置,以保证不越界。

1.3 仅仅反转字母

leetcode 917. 仅仅反转字母

这道题就是最简单的反转字符串多了一个判断

class Solution {
    public String reverseOnlyLetters(String s) {
        int left = 0;
        int right = s.length() - 1;
        char[] ch = s.toCharArray();
        while (left < right) {
            if (!Character.isLetter(ch[left])) {
                left++;
            } else if (!Character.isLetter(ch[right])) {
                right--;
            } else {
                char temp = ch[left];
                ch[left++] = ch[right];
                ch[right--] = temp;
            }
        }
        return new String(ch);
    }
}

1.4 反转字符串中的单词

leetcode 151. 反转字符串中的单词

方法一:使用api

没什么好说的

class Solution {
    public String reverseWords(String s) {
        if(s == null || s.length() == 0){
            return s;
        }
        // 去除两边的空格
        s = s.trim();
        // 根据正则表达式按照空格进行分割添加
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        // 翻转集合
        Collections.reverse(wordList);
        // 将集合的数据按照空格进行拼接
        return String.join(" ", wordList);
    }
}
方法二:自己实现上面写的功能

注意:不要使用辅助空间,空间复杂度要求为O(1)。

不能使用辅助空间之后,那么只能在原字符串上下功夫了。

想一下,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

举个例子,源字符串为:"the sky is blue "

  • 移除多余空格 : “the sky is blue”
  • 字符串反转:“eulb si yks eht”
  • 单词反转:“blue is sky the”

这样我们就完成了翻转字符串里的单词。

class Solution {
    public String reverseWords(String s) {
        // 1.去除首尾以及中间多余空格
        StringBuilder sb = removeSpace(s);
        // 2.反转整个字符串
        reverseString(sb, 0, sb.length() - 1);
        // 3.反转各个单词
        reverseEachWord(sb);
        return sb.toString();
    }

    public StringBuilder removeSpace(String s){
        int start = 0;
        int end = s.length() - 1;
        while (s.charAt(start) == ' ') start++;
        while (s.charAt(end) == ' ') end--;
        StringBuilder sb = new StringBuilder();
        while (start <= end) {
            char c = s.charAt(start);
            if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
                sb.append(c);
            }
            start++;
        }
        return sb;
    }

    public void reverseString(StringBuilder sb, int start, int end) {
        while (start < end) {
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
        }
    }

    private void reverseEachWord(StringBuilder sb) {
        int start = 0;
        int end = 1;
        int n = sb.length();
        while (start < n) {
            while (end < n && sb.charAt(end) != ' ') {
                end++;
            }
            reverseString(sb, start, end - 1);
            start = end + 1;
            end = start + 1;
        }
    }
}

2 验证回文串

leetcode 125. 验证回文串

方法一:分步实现

这题不难,也就是先把string的大写变成小写,保留数字,其他的去掉,然后再进行判断

那也就是分为两步即可

lass Solution {
    public boolean isPalindrome(String s) {
        String sb = toLowerCase(s);
        int left = 0;
        int right = sb.length() - 1;
        while(left < right){
            if(sb.charAt(left++) != sb.charAt(right--)){
                return false;
            }
        }
        return true;
    }

    public String toLowerCase(String s){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c >= 'A' && c <= 'Z') {
                sb.append((char) (c + 32));
            } else if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

这样是分成两步来完成,不过也可以一步到位

方法二:一步到位

思想:

  • 可以使用双指针的方法,同时遍历字符串的左右两端进行比较。

  • 如果字符不相等,则直接返回false;如果字符相等,则继续向中间靠拢。

在该方法中,我们使用了Character.isLetterOrDigit()来判断字符是否是字母或数字,并使用Character.toLowerCase()将字符转换为小写字母进行比较。通过逐步移动左右指针来比较字符,如果有不相等的情况,直接返回false;如果遍历完字符串没有返回false的情况,则返回true,表示字符串是回文。

class Solution {
    public boolean isPalindrome(String s) {
        int left = 0;
        int right = s.length() - 1;

        while (left < right) {
            char c1 = s.charAt(left);
            char c2 = s.charAt(right);

            if (!Character.isLetterOrDigit(c1)) {
                left++;
            } else if (!Character.isLetterOrDigit(c2)) {
                right--;
            } else {
                if (Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
                    return false;
                }
                left++;
                right--;
            }
        }
        
        return true;
    }
}

3 字符串中的第一个唯一字符

leetcode 387. 字符串中的第一个唯一字符

思路:

  • 遍历字符串,记录每个字符的个数
  • 查看每个字符的个数,如果为1,就是唯一字符,并且顺序查看时,第一个就是第一个唯一字符

那么根据这个思路,就需要一个数据结构能够记录字符的个数,还能够顺序存储,所以使用LinkedHashMap

算法如下:

class Solution {
    public int firstUniqChar(String s) {
        HashMap<Character, Integer> map = new LinkedHashMap<>();
        for (char c : s.toCharArray()) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        for (char c : map.keySet()) {
            if (map.get(c) == 1) {
                return s.indexOf(c);
            }
        }
        return -1;
    }
}

当然使用hashmap也可以,就是慢很多,这里也附上:

class Solution {
    public int firstUniqChar(String s) {
        Map<Character, Integer> frequency = new HashMap<Character, Integer>();
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            frequency.put(ch, frequency.getOrDefault(ch, 0) + 1);
        }
        for (int i = 0; i < s.length(); ++i) {
            if (frequency.get(s.charAt(i)) == 1) {
                return i;
            }
        }
        return -1;
    }
}

4 有效的字母异位词

leetcode 242. 有效的字母异位词

方法1:哈希

这题跟上一天几乎一样,只不过这一题多一个string,那么只要减去第二个string存在的个数,再判断map里的keyvalue是否为0即可

class Solution {
    public boolean isAnagram(String s, String t) {
        HashMap<Character, Integer> map = new LinkedHashMap();
        for(char c : s.toCharArray()){
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        for(char c : t.toCharArray()){
            if(!map.containsKey(c)){
                return false;
            }
            map.put(c, map.get(c) - 1);
        }
        for (char key : map.keySet()) {
            if(map.get(key) != 0){
                return false;
            }
        }
        return true;
    }
}

方法2:数组

通过使用固定大小的数组来统计字符出现的次数,这种方法避免了使用哈希表的额外空间开销,并提高了时间复杂度。

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }

        int[] count = new int[26];

        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }

        for (char c : t.toCharArray()) {
            count[c - 'a']--;
        }

        for (int i : count) {
            if (i != 0) {
                return false;
            }
        }

        return true;
    }
}

方法3:排序数组

如果两个字符串是字母异位词,那么它们排序后的结果应该是相同的。

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }

        char[] sChars = s.toCharArray();
        char[] tChars = t.toCharArray();

        Arrays.sort(sChars);
        Arrays.sort(tChars);

        return Arrays.equals(sChars, tChars);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值