剑指 offer——字符串篇
05. 替换空格
题意:面试题05. 替换空格
思路:题目要求将空格字符‘ ’替换为‘20%’,即将一个字符替换为三个。我们可以先遍历一遍字符串,统计出字符串中空格的个数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;
}
}