Skip to content

Commit 81632f0

Browse files
committed
🎨 update: string
1 parent 38072d2 commit 81632f0

File tree

7 files changed

+410
-0
lines changed

7 files changed

+410
-0
lines changed

docs/.vuepress/config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ module.exports = {
8686
'/dataStructure/栈和队列/栈的压入弹出序列',
8787
]
8888
},
89+
{
90+
title: '字符串',
91+
children: [
92+
'/dataStructure/字符串/表示数值的字符串',
93+
'/dataStructure/字符串/替换空格',
94+
'/dataStructure/字符串/正则表达式匹配',
95+
'/dataStructure/字符串/字符串的排列',
96+
'/dataStructure/字符串/字符串翻转',
97+
'/dataStructure/字符串/字符流中第一个不重复的字符',
98+
]
99+
},
89100
]
90101
}
91102
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
{
3+
"title": "字符串的排列",
4+
}
5+
---
6+
7+
## 题目
8+
9+
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串`abc`,则打印出由字符`a,b,c`所能排列出来的所有字符串`abc,acb,bac,bca,cab``cba`
10+
11+
## 思路
12+
13+
使用回溯法
14+
15+
记录一个字符(`temp`),用于存储当前需要进入排列的字符
16+
17+
记录一个字符串(`current`),用于记录当前已经排列好的字符
18+
19+
记录一个队列(`queue`),用于存储还未被排列的字符
20+
21+
- 每次排列将`temp`添加到`current`
22+
- 如果`queue`为空,则本次排列完成,将`curret`加入到结果数组中,结束递归
23+
- 如果`queue`不为空,说明还有未排列的字符
24+
- 递归排列`queue`中剩余的字符
25+
- 为了不影响后续排列,每次递归完成,将当前递归的字符`temp`加回队列
26+
27+
## 代码
28+
29+
> 记录一个当前排列字符temp
30+
31+
```js
32+
function Permutation(str) {
33+
const result = [];
34+
if (str) {
35+
queue = str.split('')
36+
PermutationCore(queue, result);
37+
}
38+
result.sort();
39+
return [... new Set(result)];
40+
}
41+
42+
function PermutationCore(queue, result, temp = "", current = "") {
43+
current += temp;
44+
if (queue.length === 0) {
45+
result.push(current);
46+
return;
47+
}
48+
for (let i = 0; i < queue.length; i++) {
49+
temp = queue.shift();
50+
PermutationCore(queue, result, temp, current);
51+
queue.push(temp);
52+
}
53+
}
54+
```
55+
56+
> 记录一个当前索引,不断交换数组中的元素(不太好理解,不推荐)
57+
58+
```js
59+
function Permutation(str) {
60+
var result = [];
61+
if (!str) {
62+
return result;
63+
}
64+
var array = str.split('');
65+
permutate(array, 0, result);
66+
result.sort();
67+
return [... new Set(result)];
68+
}
69+
70+
function permutate(array, index, result) {
71+
if (array.length - 1 === index) {
72+
result.push(array.join(''));
73+
}
74+
for (let i = index; i < array.length; i++) {
75+
swap(array, index, i);
76+
permutate(array, index + 1, result);
77+
swap(array, i, index);
78+
}
79+
}
80+
```
81+
82+
## 考察点
83+
84+
- 字符串
85+
- 回溯算法
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
{
3+
"title": "字符串翻转",
4+
}
5+
---
6+
7+
## 题目1-翻转单词顺序
8+
9+
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串`"I am a student."`,则输出`"student. a am I"`
10+
11+
## 代码
12+
13+
直接调用数组`API`进行翻转,没啥好讲的。
14+
15+
```js
16+
function ReverseSentence(str)
17+
{
18+
if(!str){return ''}
19+
return str.split(' ').reverse().join(' ');
20+
}
21+
```
22+
23+
## 题目2-左旋转字符串
24+
25+
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如输入字符串`"abcdefg"`和数字`2`,该函数将返回左旋转2位得到的结果`"cdefgab"`
26+
27+
## 代码
28+
29+
将两个`str`进行拼接,直接从第`n`位开始截取,就相当于将前面`n`个数字移到末尾。
30+
31+
```js
32+
function LeftRotateString(str, n)
33+
{
34+
if(str&&n!=null){
35+
return (str+str).substr(n)
36+
}else{
37+
return ''
38+
}
39+
}
40+
```
41+
## 剑指offer中的思路
42+
43+
上面两个问题都可以用简单的方法解决,《剑指offer》中的解法稍微有些复杂,我不推荐用,但是思路可以参考下:
44+
45+
### 翻转单词顺序:
46+
47+
- 第一步将整个字符串翻转,`"I am a student."` -> `".tneduts a ma I"`
48+
49+
- 第二步将字符串内的单个字符串进行翻转:`".tneduts a ma I"` -> `"student. a am I"`
50+
51+
### 左旋转字符串:
52+
53+
`"abcdefg"`为例,将字符串分为两部分`ab``cdefg`
54+
55+
将两部分分别进行翻转,得到 -> `"bagfedc"`
56+
57+
再将整个字符串进行翻转,得到 -> `"cdefgab"`
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
{
3+
"title": "字符流中第一个不重复的字符",
4+
}
5+
---
6+
7+
## 题目
8+
9+
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。
10+
当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
11+
12+
如果当前字符流没有存在出现一次的字符,返回#字符。
13+
14+
## 思路
15+
16+
要求获得第一个只出现一次的。
17+
18+
~~使用一个有序的存储结构为每个字符计数,再遍历这个对象,第一个出现次数为1的即为结果。~~
19+
20+
~~在JavaScript中有序存储空间选择对象即可。~~
21+
22+
上述解决办法是有问题的,因为在`JavaScript`中对象遍历并不是在所有浏览器中的实现都是有序的,而且直接使用对象存储,当字符流中出现数字时也是有问题的。
23+
24+
所以下面改用剑指offer中的解法:
25+
26+
- 创建一个长度为`256`的数组`container`来标记字符流中字符出现的次数
27+
28+
- 使用字符`ASCII`码作为下标,这样数组长度最大为`256`
29+
30+
- 当字符没有出现过,标记为`-1`
31+
32+
- 当字符只出现一次,标记为字符在字符流中的位置`index`
33+
34+
- 当字符出现多次时,标记为`-2`
35+
36+
- 当调用`FirstAppearingOnce`时,只需要找到,数组值大于`-1`的且值最小的位置索引,即为第一个出现次数为`1`的字符
37+
38+
39+
40+
## 代码
41+
42+
```js
43+
let container = new Array(256).fill(-1);
44+
let index = 0;
45+
function Init() {
46+
container = new Array(256).fill(-1);
47+
index = 0;
48+
}
49+
function Insert(ch) {
50+
const code = ch.charCodeAt(0);
51+
if (container[code] === -1) {
52+
container[code] = index;
53+
} else if (container[code] >= 0) {
54+
container[code] = -2;
55+
}
56+
index++;
57+
}
58+
function FirstAppearingOnce() {
59+
let minIndex = 256;
60+
let strIndex = 0;
61+
for (let i = 0; i < 256; i++) {
62+
if (container[i] >= 0 && container[i] < minIndex) {
63+
minIndex = container[i];
64+
strIndex = i;
65+
}
66+
}
67+
return minIndex === 256 ? '#' : String.fromCharCode(strIndex);
68+
}
69+
```
70+
71+
72+
## 考察点
73+
74+
- 字符串
75+
- hash
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
{
3+
"title": "替换空格",
4+
}
5+
---
6+
7+
## 题目
8+
9+
请实现一个函数,将一个字符串中的每个空格替换成`“%20”`。例如,当字符串为`We Are Happy`。则经过替换之后的字符串为`We%20Are%20Happy`
10+
11+
12+
## 代码
13+
14+
1.直接用空格将字符串切割成数组,再用`20%`进行连接。
15+
16+
```js
17+
function replaceSpace(str)
18+
{
19+
return str.split(' ').join('%20');
20+
}
21+
```
22+
23+
2.用正则表达式找到所有空格依次替换
24+
25+
```js
26+
function replaceSpace(str)
27+
{
28+
return str.replace(/\s/g,'%20');
29+
}
30+
```
31+
32+
## 拓展
33+
34+
允许出现多个空格,多个空格用一个`20%`替换:
35+
36+
用正则表达式找到连续空格进行替换
37+
38+
```js
39+
function replaceSpace(str)
40+
{
41+
return str.replace(/\s+/g,'%20');
42+
}
43+
```
44+
45+
46+
## 考察点
47+
48+
- 字符串
49+
- 正则
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
{
3+
"title": "正则表达式匹配",
4+
}
5+
---
6+
7+
## 题目
8+
9+
请实现一个函数用来匹配包括'.'和'*'的正则表达式。
10+
模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。
11+
例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。
12+
13+
## 思路
14+
15+
当模式中的第二个字符不是“*”时:
16+
- 1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。
17+
- 2、如果 字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。
18+
19+
而当模式中的第二个字符是“*”时:
20+
- 如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。
21+
22+
如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:
23+
- 1、模式后移2字符,相当于x*被忽略;
24+
- 2、字符串后移1字符,模式后移2字符;
25+
- 3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位;
26+
27+
## 代码
28+
29+
```js
30+
function match(s, pattern) {
31+
if (s == undefined || pattern == undefined) {
32+
return false;
33+
}
34+
return matchStr(s, pattern, 0, 0);
35+
}
36+
37+
function matchStr(s, pattern, sIndex, patternIndex) {
38+
if (sIndex === s.length && patternIndex === pattern.length) {
39+
return true;
40+
}
41+
if (sIndex !== s.length && patternIndex === pattern.length) {
42+
return false;
43+
}
44+
if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] === '*') {
45+
if (sIndex < s.length && (s[sIndex] === pattern[patternIndex] || pattern[patternIndex] === '.')) {
46+
return matchStr(s, pattern, sIndex, patternIndex + 2) ||
47+
matchStr(s, pattern, sIndex + 1, patternIndex + 2) ||
48+
matchStr(s, pattern, sIndex + 1, patternIndex);
49+
} else {
50+
return matchStr(s, pattern, sIndex, patternIndex + 2)
51+
}
52+
}
53+
if (sIndex < s.length && (s[sIndex] === pattern[patternIndex] || pattern[patternIndex] === '.')) {
54+
return matchStr(s, pattern, sIndex + 1, patternIndex + 1)
55+
}
56+
return false;
57+
}
58+
```
59+
60+
## 考察点
61+
62+
- 字符串
63+
- 正则表达式
64+
- 考虑问题的全面性
65+
- 程序的完整性

0 commit comments

Comments
 (0)