剑指offer-字符串问题

1. 字符串的排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

解题思路
利用递归的思想,首先固定第一个字符,将剩余的部分看做一个新的字符串,递归的取得首位后面各种字符串的组合。再第一个字符串与后面的每一个字符串进行交换,同样递归的获得其字符串组合。
1.png

代码实现

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  
import java.util.ArrayList;
import java.util.Collections;
// 使用数组
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> ret = new ArrayList<String>();
if(str.length()==0 && str==null){
return ret;
}
Permute(str.toCharArray(), 0, ret);
Collections.sort(ret);
return ret;
}
public void Permute(char[] str, int i, ArrayList<String> ret){
if(i == str.length - 1){
if(!ret.contains(new String(str))){
ret.add(new String(str));
}
}else{
for(int j=i; j<str.length; j++){
swap(str, i, j);
Permute(str, i+1, ret);
swap(str, i, j);
}
}
}
public void swap(char[] str, int i, int j){
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
// 使用StringBuffer
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> ret = new ArrayList<String>();
if(str.length()==0 && str==null){
return ret;
}
Permute(new StringBuffer(str), 0, ret);
Collections.sort(ret);
return ret;
}
public void Permute(StringBuffer str, int i, ArrayList<String> ret){
if(i == str.length() - 1){
if(!ret.contains(str.toString())){
ret.add(str.toString());
}
}else{
for(int j=i; j<str.length(); j++){
swap(str, i, j);
Permute(str, i+1, ret);
swap(str, i, j);
}
}
}
public void swap(StringBuffer str, int i, int j){
char temp = str.charAt(i);
str.setCharAt(i, str.charAt(j));
str.setCharAt(j, temp);
}
}
// 算法时间复杂度: O(n!)
// 空间复杂度:O(1)

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

题目描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

解题思路
遍历一遍字符串,统计每个字符出现的次数。然后再遍历一遍字符串,找出答案。

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
37
38
39
40
41
  
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str==null || str.length()==0){
return -1;
}
HashMap<Character, Integer> map = new HashMap<>();
for(int i=0; i<str.length(); i++){
if(!map.keySet().contains(str.charAt(i))){
map.put(str.charAt(i), 1);
}else{
map.put(str.charAt(i), map.get(str.charAt(i))+1);
}
}
for(int i=0; i<str.length(); i++){
if(map.get(str.charAt(i))==1){
return i;
}
}
return -1;
}
}
// ************** 使用数组实现 **************
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str==null || str.length()==0){
return -1;
}
int[] count = new int[128];
for(int i=0; i<str.length(); i++){
count[str.charAt(i)] ++;
}
for(int i=0; i<str.length(); i++){
if(count[str.charAt(i)] == 1){
return i;
}
}
return -1;
}
}

3. 左旋转字符串

题目描述

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。

解题思路

1
2
3
4
5
6
7
8
9
10
11
12
  
public class Solution {
public String LeftRotateString(String str,int n) {
if(n > str.length()){
return str;
}
int k = n % str.length();
String s1 = str.substring(0, k);
String s2 = str.substring(k);
return s2+s1;
}
}

4. 翻转单词顺序列

题目描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

解题思路

题目抽象:给定一个首尾可能带空格的字符串,请让你翻转该字符串。首尾不能有多余空格。如果全部是空格,请返回原字符串。 然后以空格为分割单元将单词分开并按正常顺序拼接。

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
    
public class Solution {
public String ReverseSentence(String str) {
//判断str是否为空
if(str.isEmpty()) return str;
//判断str中是否全为空格
int i=0, size=str.length();
while(i<size && str.charAt(i)==' ') i++;
if(i == size) return str;
String ret = "";
String tmp = "";
boolean hasstr = false;
for(i=size-1; i>=0; i--){
char c = str.charAt(i);
if(c != ' '){
tmp = c + tmp;
hasstr = true;
}else if(c==' ' && hasstr){
ret = ret + tmp + " ";
tmp = "";
hasstr = false;
}
}
if(tmp != "")
ret = ret + tmp;
return ret;
}
}

5. 扑克牌顺子

题目描述

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…..LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

解题思路

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
import java.util.Arrays;
public class Solution {
public boolean isContinuous(int [] numbers) {
if(numbers.length == 0)
return false;
Arrays.sort(numbers);
int zeor_num = 0;
int gap = 0;
for(int i=0; i<numbers.length-1; i++){
if(numbers[i] == 0){
zeor_num += 1;
continue;
}
if(numbers[i+1] == numbers[i])
return false;
int g = numbers[i+1]-numbers[i];
if(g > 1)
gap = gap + g - 1;
}
if(zeor_num >= gap){
return true;
}
return false;
}
}

6. 把字符串转换成整数

题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

输入描述

输入一个字符串,包括数字字母符号,可以为空

输出描述

如果是合法的数值表达则返回该数字,否则返回0

解题思路
这道题难点在于边界的考察。对于一般规则的数字“字符串”转化为数字如下:
1.JPG

int的范围为[2^31-1, -2^31]
如果超过了这两个范围该怎么办?
其实也很简单,首先判断这个数的正负,如果正数,超过了Integer.MAX_VALUE,就设置Integer.MAX_VALUE,如果是负数,首先我们不考虑负号,如果超过了Integer.MAX_VALUE, 则就置为Integer.MAX_VALUE+1, 最后再根据正负号,来加负号。
2.JPG

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
  
public class Solution {
public int StrToInt(String str) {
if(str == null || "".equals(str.trim())){
return 0;
}
int l = str.length();
//if(l == 0) return 0;
int i = 0;
int ret = 0;
int flag = 1;
char[] arr = str.toCharArray();
if(arr[i] == '-'){
flag = -1;
}
if(arr[i] == '+' || arr[i] == '-'){
i++;
}
while(i < l){
if(arr[i] >= '0' && arr[i] <= '9'){
int cur = arr[i] - '0';
if(flag==1 && (ret>Integer.MAX_VALUE/10 || (ret==Integer.MIN_VALUE/10 && cur > 7))){
return 0;
}
if(flag==-1 && (ret>Integer.MAX_VALUE/10 || (ret==Integer.MIN_VALUE/10 && cur > 8))){
return 0;
}
ret = ret * 10 + cur;
i++;
}else{
return 0;
}
}
return ret * flag;
}
}

7. 正则表达式匹配

题目描述
请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”abaca”匹配,但是与”aa.a”和”ab*a”均不匹配

解题思路
1.JPG
2.JPG
3.JPG

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
  
public class Solution {
public class Solution {
public boolean match(char[] str, char[] pattern){
boolean flag = imatch(str, pattern, 0, 0);
return flag;
}
public boolean imatch(char[] str, char[] pattern, int s, int p){
if(s==str.length && p==pattern.length)
return true;
if(s<str.length && p>=pattern.length)
return false;
if(p+1 < pattern.length && pattern[p+1]=='*'){
if((s < str.length && pattern[p] == '.') || (s < str.length && str[s] == pattern[p])){
return imatch(str, pattern, s, p+2) || imatch(str, pattern, s+1, p);
}else{
return imatch(str,pattern, s, p+2);
}
}
if(s < str.length && (str[s] == pattern[p] || pattern[p] == '.')){
return imatch(str, pattern, s+1, p+1);
}
return false;
}
}