文章目錄
一、環境搭建
環境搭建主要支持以下內容:
- ES6
- Jest:令人愉快的 JavaScript 測試
- npm
- git
搭建過程可以參考文章:【筆記】再學JavaScriptES(6-10)全版本語法——課程介紹與環境搭建
兩個很重要的文件:
- .babelrc:配置對ES6的支持
- .eslintrc.js:代碼格式檢查
mkdir leetcode
cd leetcode
npm init -y
1.安裝
cnpm i -D jest
cnpm i babel-jest @babel/core regenerator-runtime @babel/preset-env @babel/preset-react
編輯package.json
{
"scripts": {
"test": "jest"
}
}
編輯.babelrc
{
"presets": ["@babel/react","@babel/env"]
}
2. 創建js文件
新建code\sum.js
function sum(a, b) {
return a + b;
}
export default sum;
新建test\sum.test.js
import sum from './index'
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
3. 啓動測試
npm test
執行結果
若報錯:Plugin/Preset files are not allowed to export objects,only functions
處理方法
(1)將所有有關babel的包都升級爲7.0版本
- “@babel/core”: “^7.2.2”,
- “@babel/preset-env”: “^7.3.1”,
- “@babel/preset-react”: “^7.0.0”,
- “babel-loader”: “^8.0.5”,
並且修改.babel文件
- {“presets”:["@babel/react","@babel/env",]}
(2)降級到babel6.0版本
- “babel-core”: “^6.26.3”,
- “babel-preset-env”: “^1.7.0”,
- “babel-preset-react”: “^6.24.1”,
- “babel-loader”: “^7.1.5”,
對應修改.babelrc文件
- {“presets”: [“react”, “env”]}
babel捨棄了以前的babel--的命名方式,改成了@babel/-。修改依賴和.babelrc文件後就能正常啓動項目了。babel-core7.0之後,包名升級爲@babel/core。
二、反轉單詞原理
1.題目
給定一個字符串,你需要反轉字符串中每個單詞的字符順序,同時仍保留空格和單詞的初始順序。
示例 1:
輸入: “Let’s take LeetCode contest”
輸出: “s’teL ekat edoCteeL tsetnoc”
注意:在字符串中,每個單詞由單個空格分隔,並且字符串中不會有任何額外的空格。
2.思路分析
先使用split將字符串拆解爲單詞放入數組中,然後使用map對數組進行遍歷執行操作(split單詞拆分爲單字符數組、reverse反轉、join拼爲單詞),最後使用join拼成字符串
依次調用split、map、split、reverse、join、join
3.所用到的方法
String.prototype.split:字符串(按字符)拆爲數組
Array.prototype.map:對數組進行遍歷並對每項執行操作
Array.prototype.reverse:數組順序反轉
Array.prototype.join:數組(按字符)拼爲字符串
4.題解及優化
let reverseWords = function (s) {
let arr = s.split(' ')
let result = arr.map(item => item.split('').reverse().join(''))
return result.join(' ')
}
優化(去除中間變量,節約內存)
let reverseWords = function (s) {
return s.split(' ').map(item => item.split('').reverse().join('')).join(' ')
}
其他解法(使用了rest參數,但相對耗時)
let reverseWords = function (s) {
return s.split(' ').map(i => [...i].reverse().join('')).join(' ')
}
我的最初題解。。。
let reverseWords = function (s) {
// 先將字符串拆解爲單詞放入數組中,數組中元素的順序就是單詞的原有順序
let arr = s.split(' ')
let str = ''
let str1 = ''
let arr1 = []
console.log(arr)
// 將數組中的每一項倒序
for (let item of arr) {
for (let i = 0; i < item.length; i++) {
str = str.concat(item.substring(item.length - i - 1, item.length - i))
console.log(str)
}
// 將翻轉後的字符串合併到一個數組中並用空格分開轉爲字符串
arr1.push(str)
str1 = arr1.join(' ')
str = ''
console.log(str1)
}
return str1
}
const s = "Let's take LeetCode contest"
console.log(reverseWords(s))
三、計數二進制子串
1.題目
給定一個字符串 s,計算具有相同數量0和1的非空(連續)子字符串的數量,並且這些子字符串中的所有0和所有1都是組合在一起的。
重複出現的子串要計算它們出現的次數。
示例 1 :
輸入: “00110011”
輸出: 6
解釋: 有6個子串具有相同數量的連續1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
請注意,一些重複出現的子串要計算它們出現的次數。
另外,“00110011”不是有效的子串,因爲所有的0(和1)沒有組合在一起。
示例 2 :
輸入: “10101”
輸出: 4
解釋: 有4個子串:“10”,“01”,“10”,“01”,它們具有相同數量的連續1和0。
注意:
- s.length 在1到50,000之間。
- s 只包含“0”或“1”字符。
2.思路分析
類似於數學幾何題畫輔助線,我們得到如下圖譜:
3.所用到的方法
見題解
4.題解及優化
課程提供的解法(有bug,通不過)
let countBinarySubstrings = (s) => {
// 建立數據結構,堆棧,保存數據
let r = []
// 給定任意子輸入都返回第一個符合條件的子串
let match = (s) => {
let j = s.match(/^(0+|1+)/)[0]
let o = (j[0] ^ 1).toString().repeat(j.length)
let reg = new RegExp(`^(${j}${o})`)
if (reg.test(s)) {
return RegExp.$1
} else {
return ''
}
}
// 通過for循環控制程序運行的流程
for (let i = 0, len = s.length - 1; i < len; i++) {
let sub = match(s.slice(i)) // 返回從第i位開始的字符串
if (sub) {
r.push(sub)
}
}
return r.length
}
這種解法超長測試用例會導致RegExp too big的問題
下面推薦幾種LeetCode小夥伴的解法:
巧妙解法1
let countBinarySubstrings = (s) => {
let n = 0, arr = s.match(/([1]+)|([0]+)/g) // 對於0和1分組捕獲
for (let i = 0; i < arr.length - 1; i++) {
// 統計每相鄰兩組之間元素長度的最小值,即爲這兩組中符合條件的子串個數
n += Math.min(arr[i].length, arr[i + 1].length)
}
return n
}
因爲已知
- 000111必定有三個子串
- 00011必定有兩個子串
- 0111必定有1個子串
以此類推, 每兩組數據之間長度最短的值爲子串的數量
先統計連續的0和1分別有多少個,如:111100011000,得到4323;在4323中的任意相鄰兩個數字,取小的一個加起來,就是3+2+2 = 7
巧妙解法2
let countBinarySubstrings = function (s) {
// pre 前一個數字連續出現的次數,curr 當前數字連續出現的次數,n 統計子串個數
let n = 0, pre = 0, curr = 1
for (let i = 0, len = s.length; i < len - 1; i++) {
if (s[i] == s[i+1]) {
// 相同,則當前數字出現的次數curr加1
curr++
} else {
// 不同,則當前數字事實上變成了前一個數字,當前數字的次數重置爲1
pre = curr
curr = 1
}
// 前一個數字出現的次數 >= 後一個數字出現的次數,則一定包含滿足條件的子串
if (pre >= curr) n++
}
return n
}
以00110011爲測試用例:
i | pre | curr | n | 說明 |
---|---|---|---|---|
0 | 0 | 1 | 0 | 初始狀態 |
0 | 0 | 2 | 0 | √,首次循環 |
1 | 2 | 1 | 1 | × |
2 | 2 | 2 | 2 | √ |
3 | 2 | 1 | 0 | × |
4 | 2 | 2 | 0 | √ |
5 | 2 | 1 | 0 | × |
6 | 2 | 2 | 0 | √ |
其他常規解法1
var countBinarySubstrings = function(s) {
if (s.length < 2) return 0;
// 模擬隊列
let queue = [], i = 1, res = 0;
queue.push(s[0]);
while (i < s.length) {
// 如果隊頭元素與當前元素相等
if (queue[0] == s[i]) {
let last = queue[queue.length - 1];
queue.push(s[i]);
// 如果隊列尾部元素與當前元素不等,則必須計算一次,且重置隊列
if (last != s[i]) {
res++;
let index = queue.indexOf(last);
queue = queue.slice(index + 1);
}
} else {
// 如果與隊列頭不相等
res++;
queue.shift();
queue.push(s[i]);
}
i++;
}
return res;
};
i | queue | last | res | 說明 |
---|---|---|---|---|
1 | 0 | 0 | 0 | 初始狀態 |
1 | 00 | 0 | 0 | 首次循環 |
2 | 01 | 1 | shift | |
3 | 11 | 2 | shift | |
4 | 10 | 3 | shift | |
5 | 00 | 4 | shift | |
6 | 01 | 5 | shift | |
7 | 11 | 6 | shift |
其他常規解法2
比較耗時
var countBinarySubstrings = function(s) {
let result = 0;
// 字符串匹配算法
const match = (subString) => {
// 先找到開頭的連續數字[0|1]
let startStr = subString.match(/^((0+)|(1+))/)[0]
subString = subString.slice(0, startStr.length * 2)
// 推算出組合字符串
let endStr = (startStr[0] ^ 1).toString().repeat(startStr.length)
// 查看字符串中是否匹配組合字符串
return subString.startsWith(`${startStr}${endStr}`)
}
// 循環計算每個子串中出現符合條件的字符情況,如果找到就+1,並break到下一個子串
for (let index = 0; index < s.length-1; index++) {
let subString = s.slice(index)
if (match(subString)) {
result += 1;
}
}
return result
};
拓展: