剑指 offer——字符串篇

Posted yzihan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 offer——字符串篇相关的知识,希望对你有一定的参考价值。

05. 替换空格

题意:面试题05. 替换空格
思路:题目要求将空格字符‘ ’替换为‘20%’,即将一个字符替换为三个。我们可以先遍历一遍字符串,统计出字符串中空格的个数count,根据这个数字可以计算出:

[替换之后字串的长度 = 原字串长度 + 2 * count ]

然后从后向前,依次将原字符串的非空格内容复制到新的字符数组中,遇到空格,则依次添加‘%’、‘0’、‘2’。

class Solution {
    public String replaceSpace(String s) {
        char[] arr = s.toCharArray();
        int count = 0;
        for (int i = 0; i < arr.length; i ++) {
            if (arr[i] == ‘ ‘) {
                count ++;
            }
        }
        char[] res = new char[arr.length + count * 2];
        int index = res.length - 1;
        int i = arr.length - 1;
        while (i >= 0) {
            if (arr[i] == ‘ ‘) {
                res[index --] = ‘0‘;
                res[index --] = ‘2‘;
                res[index --] = ‘%‘;
            } else {
                res[index --] = arr[i];
            }
            i --;
        }
        return String.valueOf(res);
    }
}

20. 表示数值的字符串

题意:[面试题20. 表示数值的字符串](https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/)
思路:参照题解有限状态机DFA
技术图片
技术图片

class Solution {
    int[][] transTable = {
        {1, 2, 7, -1, -1, 0,},
        {-1, 2, 7, -1, -1, -1},
        {-1, 2, 3, 4, -1, 9},
        {-1, 3, -1, 4, -1, 9},
        {6, 5, -1, -1, -1, -1},
        {-1, 5, -1, -1, -1, 9},
        {-1, 5, -1, -1, -1, -1},
        {-1, 8, -1, -1, -1, -1},
        {-1, 8, -1, 4, -1, 9},
        {-1, -1, -1, -1, -1, 9}
    };

    Map<String, Integer> indexMap = new HashMap<String, Integer>() {
        {
            put("sign", 0);
            put("number", 1);
            put(".", 2);
            put("exp", 3);
            put("other", 4);
            put("blank", 5);
        }
    };

    Set<Integer> set = new HashSet<>(Arrays.asList(2, 3, 5, 8, 9));

    public boolean isNumber(String s) {
        int state = 0;
        for (char c : s.toCharArray()) {
            state = transTable[state][nextState(c)];
            if (state == -1) {
                return false;
            }
        }
        return set.contains(state);
    }

    private int nextState(char c) {
        String name;
        if (c >= ‘0‘ && c <= ‘9‘) {
            name = "number";
        } else if (c == ‘+‘ || c == ‘-‘) {
            name = "sign";
        } else if (c == ‘.‘) {
            name = ".";
        } else if (c == ‘E‘ || c == ‘e‘) {
            name = "exp";
        } else if (c == ‘ ‘) {
            name = "blank";
        } else {
            name = "other";
        }
        return indexMap.get(name);
    }
}

38. 字符串的排列

题意:面试题38. 字符串的排列
思路:递归构建。每次固定一个位置上的值,然后让其后面位置上的元素进行全排列。固定某一个位置的元素可以使用交换的方式。

class Solution {
    public String[] permutation(String s) {
        char[] arr = s.toCharArray();
        Set<String> res = new HashSet<>();
        permutation(arr, 0, res);
        return res.toArray(new String[0]);
    }

    private void permutation(char[] arr, int start, Set<String> res) {
        if (start == arr.length) {
            res.add(String.valueOf(arr));
            return;
        }
        for (int i = start; i < arr.length; i ++) {
            swap(arr, i, start);
            permutation(arr, start + 1, res);
            swap(arr, i, start);
        }
    }

    private void swap(char[] arr, int i, int j) {
        char tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

对于有重复元素的字符串,还可以使用以下做法。某一个位置上固定的元素只需要固定一次就行。

class Solution {
    public String[] permutation(String s) {
        char[] arr = s.toCharArray();
        Set<String> res = new HashSet<>();
        permutation(arr, 0, res);
        return res.toArray(new String[0]);
    }

    private void permutation(char[] arr, int start, Set<String> res) {
        if (start == arr.length) {
            res.add(String.valueOf(arr));
            return;
        }
        for (int i = start; i < arr.length; i ++) {
            if (i > start && arr[i] == arr[start]) {
                continue;
            }
            swap(arr, i, start);
            permutation(arr, start + 1, res);
            swap(arr, i, start);
        }
    }

    private void swap(char[] arr, int i, int j) {
        char tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

48. 最长不含重复字符的子字符串

题意:面试题48. 最长不含重复字符的子字符串
思路:滑动窗口。使用两个指针指向滑动窗口的左右边界,先移动右边界,直到出现重复字符,再移动左边界,直到消除重复字符。重复以上过程直到右边界到达字串的最后位置。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> set = new HashSet<>();
        char[] arr = s.toCharArray();
        int left = -1;
        int right = 0;
        int max = 0;
        while (right < arr.length) {
            if (set.contains(arr[right])) {
                while (left < right) {
                    set.remove(arr[++left]);
                    if (arr[left] == arr[right]) {
                        break;
                    }
                }
            }
            set.add(arr[right]);
            max = Math.max(right - left, max);
            right ++;
        }
        return max;
    }
}

50. 第一个只出现一次的字符

题意:面试题50. 第一个只出现一次的字符
思路:使用hash表记录字串中每个字符出现的次数。然后再遍历一遍字符,找到第一个出现次数为1的字符返回即可。

class Solution {
    public char firstUniqChar(String s) {
        if (s == null || s.length() == 0) {
            return ‘ ‘;
        }
        char[] arr = s.toCharArray();
        int[] chars = new int[26];
        for (char c : arr) {
            chars[c - ‘a‘]++;
        }
        for (char c : arr) {
            if (chars[c - ‘a‘] == 1) {
                return c;
            }
        }
        return ‘ ‘;
    }
}

58-I. 翻转单词顺序

题意:面试题58 - I. 翻转单词顺序
思路:将句子以空格分隔开,然后反过来拼接即可。

import java.util.StringJoiner;
class Solution {
    public String reverseWords(String s) {
        String[] words = s.trim().split("\s+");
        StringJoiner sj = new StringJoiner(" ");
        for (int i = words.length - 1; i >= 0; i --) {
            sj.add(words[i]);
        }
        return sj.toString();
    }
}

58-II. 左旋转字符串

题意:面试题58 - II. 左旋转字符串
思路:将左右两部分分别翻转,然后字符串整体翻转即可。

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] arr = s.toCharArray();
        swap(arr, 0, n - 1);
        swap(arr, n, arr.length - 1);
        swap(arr, 0, arr.length - 1);
        return String.valueOf(arr);
    }

    private void swap(char[] arr, int start, int end) {
        if (start >= end) {
            return;
        }
        char tmp;
        while (start < end) {
            tmp = arr[start];
            arr[start] = arr[end];
            arr[end] = tmp;
            start ++;
            end --;
        }
    }
}

67. 把字符串转换成整数

题意:面试题67. 把字符串转换成整数
思路:按照以下几步转换:
1)丢弃字符串前的空格;
2)如果有正负号,则记录正负号;
3)将紧跟着的数字记录并转换为整数,这里注意要使用long类型;
4)每次将符号与数字组合与整数的最大值与最小值比较,超过则直接返回。

class Solution {
    public int strToInt(String str) {
        int i = 0;
        char[] arr = str.trim().toCharArray();
        long res = 0;
        int sign = 1;
        if (arr.length == 0) {
            return 0;
        }
        if (arr[i] == ‘-‘ || arr[i] == ‘+‘) {
            sign = arr[i] == ‘-‘ ? -1 : 1;
            i ++;
        }
        while (i < arr.length) {
            if (arr[i] >= ‘0‘ && arr[i] <= ‘9‘) {
                res = 10 * res + (arr[i ++] - ‘0‘);
                if (sign > 0 && sign * res > Integer.MAX_VALUE) {
                    return Integer.MAX_VALUE;
                } else if (sign < 0 && sign * res < Integer.MIN_VALUE) {
                    return Integer.MIN_VALUE;
                }
            } else {
                break;
            }
        }
        return sign * (int)res;
    }
}














以上是关于剑指 offer——字符串篇的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode与《代码随想录》字符串篇:做题笔记与总结-JavaScript版

LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段

剑指offer--38字符串的排列

剑指offer--38字符串的排列

《剑指Offer——58:左旋转字符串》代码

剑指 offer——贪心动态规划篇