前言:
之前一直都说想好好学习一下算法,但是,迟迟都没有行动,这次,立个flag,2021年12月底之前,学习完《算法101》。每天学习一道题,每天进步一点点,加油!
希望看完这些算法题以后,我可以收获到这些:
- 更好的逻辑思维能力;
- 对数据结构更深的理解;
- 能够写出更加牛逼的代码;
- 一份更好的工作。
下面,让我们进入字符串篇幅吧。
一、翻转整数、有效的字母异位词和翻转整数
1.1 翻转整数
**题目:**给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例:
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 。请根据这个假设,如果反转后整数溢出那么就返回 0。
方法一:翻转字符串方法
思路:
如果将数字看成是有符号的字符串,那么,我们就能通过使用JS提供的字符串方法,来实现非符号部分的翻转,又因为整数的翻转并不影响符号,所以我们最后补充符号,完成算法。
详解:
- 首先设置边界极值;
- 使用字符串的翻转函数进行主逻辑;
- 补充符号;
- 然后拼接最终结果。
代码:
/**
1. @param {number} x
2. @return {number}
*/
const reverse = (x) => {
// 非空判断
if (typeof x !== 'number') {
return;
}
// 极值
const MAX = 2147483647;
const MIN = -2147483648;
// 识别数字剩余部分并翻转
const rest =
x > 0
? String(x)
.split('')
.reverse()
.join('')
: String(x)
.slice(1)
.split('')
.reverse()
.join('');
// 转换为正常值,区分正负数
const result = x > 0 ? parseInt(rest, 10) : 0 - parseInt(rest, 10);
// 边界情况
if (result >= MIN && result <= MAX) {
return result;
}
return 0;
};
方法二:类似 欧几里得算法 求解
思路:
我们借鉴欧几里得求最大公约数的方法来解题。符号的处理逻辑同方法一,这里我们通过模10取到最低位,然后又通过乘10将最低位迭代到最高位,完成翻转。
详解:
- 设置边界极值;
- 取给定数值的绝对值,遍历循环生成每一位数字,借鉴欧几里得算法,从num的最后一位开始取值拼成新的数;
- 同步剔除掉被消费的部分;
- 如果最终结果为异常值,则直接返回0;如果,原本数据为负数,则对最终结果取反;
- 返回最终结果。
代码:
/**
* @param {number} x
* @return {number}
*/
const reverse = (x) => {
// 获取相应数的绝对值
let int = Math.abs(x);
// 极值
const MAX = 2147483647;
const MIN = -2147483648;
let num = 0;
// 遍历循环生成每一位数字
while (int !== 0) {
// 借鉴欧几里得算法,从 num 的最后一位开始取值拼成新的数
num = (int % 10) + (num * 10);
// 剔除掉被消费的部分
int = Math.floor(int / 10);
}
// 异常值
if (num >= MAX || num <= MIN) {
return 0;
}
if (x < 0) {
return num * -1;
}
return num;
};
1.2 有效的字母异位词
**题目:**给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词。
示例1:
输入: s = "anagram", t = "nagaram"
输出: true
示例2:
输入: s = "rat", t = "car"
输出: false
方法一:利用数组sort()方法
思路:
首先,对字符串字母进行排序,然后,比较两字符串是否相等。
详解:
- 首先,将字符串转为数组;
- 利用数组sort方法进行排序;
- 然后,转为字符串进行比较,如果相等返回true,反之返回false。
代码:
const isAnagram = (s, t) => {
const sArr = s.split('');
const tArr = t.split('');
const sortFn = (a, b) => {
return a.charCodeAt() - b.charCodeAt();
};
sArr.sort(sortFn);
tArr.sort(sortFn);
return sArr.join('') === tArr.join('');
};
方法二:计数累加方法
思路:
声明一个对象记录字符串每个字母的个数,另外一个字符串每项与得到的对象做匹配,最后,根据计数判断是否相等。
详解:
- 首先,声明一个变量,遍历其中一个字符串s或t,对每个字母出现的次数进行累加;
- 然后,遍历另一个字符串,使每一个字母在一得到的对象中做匹配,如果匹配对象下的字母个数减1,如果匹配不到,则返回false,如果最后对象中每个字母个数都为0,则表示两字符串相等。
代码:
const isAnagram = (s, t) => {
if (s.length !== t.length) {
return false;
}
const hash = {};
for (const k of s) {
hash[k] = hash[k] || 0;
hash[k] += 1;
}
for (const k of t) {
if (!hash[k]) {
return false;
}
hash[k] -= 1;
}
return true;
};
1.3 字符串转换整数
atoi是把字符串转换成整型数的一个函数,实现一个atoi函数,使其能将字符串转换成整数。
示例1:
输入: "42
输出: 42
示例2:
输入: "-42"
输出: -42
示例3:
输入: "4193 with words"
输出: 4193
示例4:
输入: "words and 987"
输出: 0
示例5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。 因此返回 INT_MIN (−2147483648) 。
方法一:正则匹配
思路:
第一步,使用正则提取满足条件的字符,/^(-|+)?\d+/g,(-|+)?表示第一位是-或+或都不是,\d+表示匹配多个数字;
const result = str.trim().match(/^(-|\+)?\d+/g);
第二步,判断目标是否超过Int整型的最大值或最小值;
return result
? Math.max(Math.min(Number(result[0]), Math.pow(2,31)-1), -Math.pow(2,31))
: 0;
代码:
/**
* @param {string} str
* @return {number}
*/
const myAtoi = function (str) {
// 提取需要的字符
const result = str.trim().match(/^(-|\+)?\d+/g);
return result
? Math.max(Math.min(Number(result[0]), Math.pow(2, 31) - 1), -Math.pow(2, 31))
: 0;
};
方法二:逐个判断
思路:
第一步,去除字符串之中的空格;
const news = str.trim();
第二步,通过执行parseInt判断是否为数字,不是数字返回0,是数组继续解析;
if(parseInt(news)){
return retrunNum(parseInt(news));
} else {
return 0;
}
第三步,判断目标是否超过Int整型的最大值或最小值;
const retrunNum = function (num) {
if (num >= -Math.pow(2, 31) && num <= Math.pow(2, 31) - 1) {
return num;
} else {
return num > 0 ? Math.pow(2, 31) - 1 : -Math.pow(2, 31);
}
};
代码:
/**
* @param {string} str
* @return {number}
*/
const myAtoi = function (str) {
const news = str.trim();
if (parseInt(news)) {
return retrunNum(parseInt(news));
} else {
return 0;
}
};
const retrunNum = function (num) {
if (num >= -Math.pow(2, 31) && num <= Math.pow(2, 31) - 1) {
return num;
} else {
return num > 0 ? Math.pow(2, 31) - 1 : -Math.pow(2, 31);
}
};
二、报数、反转字符串和字符串中的第一个唯一字符串
2.1 报数
**题目:**报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
**题目:**给定一个正整数n(1≤n≥30),输出报数序列的第n项。
**注意:**整数顺序将表示为一个字符串。
示例:
输入: 1
输出: "1"
输入: 4
输出: "1211"
方法一:递归
想要获取第n项的结果,需要先获取到第n-1项的结果,然后报出第n-1项的结果做为第n项的结果。所以,可以采用递归调用法。
const countAndSay = function (n) {
if (n === 1) {
return '1';
}
const preResult = countAndSay(n - 1); // 获取第 n-1 项的结果。
/**
* \d 匹配一个数字
* \1 匹配前面第一个括号内匹配到的内容
* (\d)\1* 匹配相邻数字相同的内容
* 使用replace方法将匹配到的内容处理为长度 + 内容的第一个字符
* 结果为所求报数
**/
return preResult.replace(/(\d)\1*/g, item => `${item.length}${item[0]}`);
};
方法二:循环法
递归法是由n到1计算相应的值并层层返回的,循环法正好相反,循环法由1计算到n。然后,将最终值返回。
const countAndSay = function (n) {
let result = '1'; // 第一个数为'1'
for (let i = 1; i < n; i++) { // 循环获取知道第 n 项。
// 同方法一
result = result.replace(/(\d)\1*/g, item => `${item.length}${item[0]}`);
}
return result;
};
2.2 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组char[]的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。
你可以假设数组中的所有字符都是ASCII码表中的可打印字符。
示例1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
方法一:首尾替换法
思路:
首尾替换法,逐位遍历,进行交换。
详解
- 设置变量i = 0 ;
- 替换字符串的第i位和倒数第i位,替换方式:使用es6的解构赋值进行变量的交换;
- 变量 i + 1,继续替换字符串的第i位和倒数第 i 位;
- 直到 i 大于字符串s 的长度的中位数,完全整个字符串的反转。
/**
1. @param {character[]} s
2. @return {void} Do not return anything, modify s in-place instead.
*/
const reverseString = function (s) {
for (let i = 0; i < s.length / 2; i++) {
[s[i], s[s.length - 1 - i]] = [s[s.length - 1 - i], s[i]];
}
return s;
};
方法二:中间变量首尾替换法
思路:
中间变量首尾替换法,逐位遍历,进行变换。
详解
- 设置变量 i = 0;
- 替换字符串的第i位和倒数第i位,替换方式:设置一个中间变量,替换两个字符串的值;
- 变量 i + 1,继续替换字符串的第i位和倒数第i位;
- 直到i大于字符串s的长度的中位数,完成整个字符串的反转。
/**
* @param {character[]} s
* @return {void} Do not return anything, modify s in-place instead.
*/
const reverseString = function (s) {
for (let i = 0; i < s.length / 2; i++) {
const a = s[i];
s[i] = s[s.length - i - 1];
s[s.length - i - 1] = a;
}
};
2.3 字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例:
s = "leetcode"
返回 0.
s = "loveleetcode",
返回 2.
注意事项:您可以假定该字符串只包含小写字母。
方法一:库函数
思路:
某个字符从头开始开始的索引和从尾开始找的索引如果相等,就说明这个字符只出现了一次。
详解:
- 从头到尾遍历一遍字段串;
- 判断每个位置的字符的 index() 和 lastIndexOf() 的结果是否相等;
代码:
/**
* @param {string} s
* @return {number}
*/
const firstUniqChar = function (s) {
for (let i = 0; i < s.length; i += 1) {
if (s.indexOf(s[i]) === s.lastIndexOf(s[i])) {
return i;
}
}
return -1;
};
方法二:哈希
思路:
遍历两次。第一次遍历,用一个哈希对象记录所有字符的出现次数;第二次遍历,找出哈希对象中只出现一次的字符的下标。
详解:
- 第一次遍历,用一个哈希对象记录所有字符的出现次数;
- 第二次遍历,找出哈希对象中只出现一次的字符的下标。
代码:
/**
* @param {string} s
* @return {number}
*/
const firstUniqChar = function (s) {
const hash = {};
for (let i = 0; i < s.length; i += 1) {
if (!hash[s[i]]) {
hash[s[i]] = 1;
} else {
hash[s[i]] += 1;
}
}
for (let i = 0; i < s.length; i += 1) {
if (hash[s[i]] === 1) {
return i;
}
}
return -1;
};
三、验证回文字符串、实现strStr()、最长公共前缀和最长回文子串
2.1 验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例2:
输入: "race a car"
输出: false
方法一:
思路
首先,去除字符串中的非字母和数字,再将字符串转换为数组,再对数组首尾一一比较,即可得出结果。
详解
- 将传入的字符串,利用 toLowerCase() 方法统一转化为小写,再利用正则表达式 /[ ^ A-Za-z0-9]/g 在字符串中去除非字母和数字,最后将字符串转换为数组;
- 转换数组后,利用循环一一比较元素,先比较第一个和最后一个,再比较第二个和倒数二个,依次类推,若中间有不相等则不是回文串,反之,则是回文串。
代码
/**
1. @param {string}
2. @return {boolean}
*/
const isPalindrome = (s) => {
// 将传入的字符串,统一转化为小写,同时去除非字母和数字,在转换为数组
const arr = s.toLowerCase().replace(/[^A-Za-z0-9]/g, '').split('');
let i = 0;
let j = arr.length - 1;
// 循环比较元素
while (i < j) {
// 从首尾开始, 一一比较元素是否相等
if (arr[i] === arr[j]) {
// 若相等,即第二个元素和倒数第二个元素继续比较,依次类推
i += 1;
j -= 1;
} else {
// 只要有一个相对位置上不相等,既不是回文串
return false;
}
}
// 是回文串
return true;
};
方法二:
思路
首先,去除字符串中的非字母和数字,然后,利用数组将字符串翻转,再和原字符串进行比较,即可得到结果。
详解
- 将传入的字符串,利用toLowerCase()方法统一转化为小写,再利用正则表达式 /[ ^ A-Za-z0-9]/g在字符串中去除非字母和数字,得到字符串arr ;
- 将字符串arr转换为数组,利用数组的方法反转数组,再将数组转为字符串newArr ;
- 将字符串arr和字符串newArr进行比较,相等即为回文串,不相等则不为回文串。
代码
/**
* @param {string} s
* @return {boolean}
*/
const isPalindrome = (s) => {
// 方便比较,统一转化为小写,并去除非字母和数字
const arr = s.toLowerCase().replace(/[^A-Za-z0-9]/g, '');
// 将新字符串转换为数组,利用数组的方法获得反转的字符串
const newArr = arr.split('').reverse().join('');
// 将2个字符进行比较得出结果
return arr === newArr;
};
2.2 实现strStr()
给定一个haystack字符串和一个needle字符串,在haystack字符串中找出needle字符串出现的第一个位置(从0开始)。如果不存在,则返回-1。
注:以下称 haystack 字符串为匹配字符串,needle 字符串为查找字符串。
示例1:
给定 haystack = 'hello world', needle = 'll'
返回2
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
方法一:遍历截取字符串对比
思路
截取字符串对比的思路很简单,从匹配字符串haystack中截取出与需查找字符串needle长度相等的内容后,对比截取的内容与匹配字符串相等,如果相等返回开始截取的下标。
详解
首先处理几个特殊的场景:
- needle的长度为0,直接返回0;
- needle的字符串长度大雨haystack,肯定不匹配;
- needle的字符串长度等于haystack,判断是否相等,相等则匹配,否则不匹配。
剩下的就是needle字符串长度小于haystack的情况,遍历haystack。
注:此处需要注意的是,当 haystack 剩余字符串长度小于 needle 长度时,肯定是不相等,无需再次比较。
在遍历中判断,将要截取的字符串的首位与needle字符串的首位是否相同,如果不相同也就不需要后续截取、比较,跳过该次循环。
代码
const strStr = function (haystack, needle) {
const hayLen = haystack.length;
const nedLen = needle.length;
if (!needle) {
return 0;
} if (nedLen > hayLen) {
return -1;
} else if (nedLen === hayLen) {
return haystack === needle ? 0 : -1;
} else {
for (let index = 0; index <= hayLen - nedLen; index++) {
if (haystack[index] !== needle[0]) {
continue;
}
if (haystack.substring(index, index + nedLen) === needle) {
return index;
}
}
}
return -1;
};
方法二:双层循环对比字符
思路
循环对比字符串思路也很简单,从匹配字符串haystack的不同位置开始遍历,判断其中是否含有查找字符串needle。
如:haystack 为 hello, needle 为 ll,依次判断 he、el、ll、lo是否完全和 ll 相等,相等即返回对应字符串在 haystack 中的下标。
详解
注:首先处理特殊边际情况,这块与第一种方法相同,就不再赘述。
以下为算法步骤:
- 设置最外层循环,遍历次数为0 - haystack长度减去needle的长度。剩余字符串长度小于needle长度时,肯定不匹配;
- 判断匹配字符串haystack中,该次循环使用到的字符串首尾字母是否与查找字符串needle首尾字母相同;
①不相等,直接跳过继续遍历;
②相等,执行第三步。 - 判断查找字符串needle的长度;
①长度为1,表明匹配成功,直接返回当前长字符串下标即可;
②长度大于1,执行第四步。 - 遍历对比字符串,循环判断匹配字符串haystack不同位置的字符是否与匹配字符串needle对应位置的字符相等;
①不相等时,跳出循环,进行下次循环;
②到最后一位还未跳出循环表明完全匹配,返回当前遍历次数(即查找字符串在匹配字符串中,首次出现的位置)
代码
const strStr = function (haystack, needle) {
const hayLen = haystack.length;
const nedLen = needle.length;
if (!needle) {
return 0;
} if (nedLen > hayLen) {
return -1;
} else if (nedLen === hayLen) {
return haystack === needle ? 0 : -1;
} else {
for (let hasIndex = 0; hasIndex <= hayLen - nedLen; hasIndex++) {
if (
haystack[hasIndex] === needle[0] &&
haystack[hasIndex + nedLen - 1] === needle[nedLen - 1]
) {
if (nedLen === 1) {
return hasIndex;
}
for (let nedIndex = 1; nedIndex < nedLen; nedIndex++) {
if (haystack[hasIndex + nedIndex] !== needle[nedIndex]) {
break;
}
if (nedIndex === nedLen - 1) {
return hasIndex;
}
}
}
}
}
return -1;
};
2.3 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例
输入: ["flower","flow","flight"]
输出: "fl"
方法一:递归迭代
思路
查找n个字符串的最长公共前缀,可以拆分成两步:1.查找前n - 1个字符串的最长公共前缀;2.查找m与最后一个字符串的公共前缀。
因此,我们可以得出递归公式:
$longestCommonPrefix([S1, S2, ..., Sn]) = findCommPrefix(longestCommonPrefix([S1, S2, ..., Sn-1]), Sn)$
我们只需要实现findCommPrefix方法,然后,遍历数组即可。
详解
- 获取数组中第一个字符,当做最长公共前缀保存到变量commonPrefix;
- 从数组中取出下一个字符串,与当前的最长公共前缀commonPrefix对比,得到新的最长公共前缀存到commonPrefix;
- 重复第2步遍历完整个字符串,最后得到的即使数组中所有字符串的最长公共前缀。
代码
/**
1. @param {string[]} strs
2. @return {string}
*/
const longestCommonPrefix = function (strs) {
function findCommonPrefix (a, b) {
let i = 0;
while (i < a.length && i < b.length && a.charAt(i) === b.charAt(i)) {
i++;
}
return i > 0 ? a.substring(0, i) : '';
}
if (strs.length > 0) {
let commonPrefix = strs[0];
for (let i = 1; i < strs.length; i++) {
commonPrefix = findCommonPrefix(commonPrefix, strs[i]);
}
return commonPrefix;
}
return '';
};
方法二:循环迭代
思路
最长公共前缀一定是数组中所有数组都包含的前缀子串,我们可以将任意字符串的前缀作为公共前缀,从长度0到n(n为该字符串长度),横向扫描数组中的所有字符串,看是否都有该前缀,直到找到不满足的为止。
详解
- 先假设最长公共子串的长度为1,存到变量 i 。以第一个字符串为基准,取它的第 i 个字符与数组中其他所有的字符串第 i 个字符进行比较,如果都相等,那么将最长公共子串的长度加1,否则停止查找,已找到最长公共前缀的长度,设置完成匹配标记flag为false;
- 重复第1步,直到 i 等于第一个字符串的长度,或者匹配标记flag为false;
- 返回第一个字符串的前 i 个字符,即为当前数组的最长公共前缀。
代码
/**
1. @param {string[]} strs
2. @return {string}
*/
const longestCommonPrefix = function (strs) {
if (strs.length === 0) {
return '';
}
let i = 0;
let flag = true;
while (flag) {
if (strs[0].length > i) {
const char = strs[0].charAt(i);
for (let j = 1; j < strs.length; j++) {
if (strs[j].length <= i || strs[j].charAt(i) !== char) {
flag = false;
break;
}
}
} else {
flag = false;
}
i++;
}
return strs[0].substring(0, i - 1);
};
2.4 最长回文子串
给定一个字符串s,找到s中最长的回文字符串。可以假设s的最大长度为1000 。
示例
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
方法一:动态规划法
思路
动态规划的思想,是希望把问题划分成相关联的子问题;然后从最基本的子问题出发来推导较大的子问题,直到所有的子问题都解决。
根据字符串的长度,建立一个矩阵 dp, 通过不同情况的判断条件,通过 dp[i][j] 表示 s[i] 至 s[j] 所代表的子串是否是回文子串。
详解
3. 建立矩阵dp;
4. 循环遍历字符串,根据不同的条件进行判断是否为回文子串;
5. 不同长度的子串,根据不同的条件进行判断是否回文子串;
(1)长度为1,一定回文;
(2)长度为2或3,判断首尾是否相同:s[i] === s[j];
(3)长度>3,首尾字符相同,且去掉首尾之后的子串仍然为回文:(s[i]===s[j] && dp[i+1][j+1]);
- 取得长度最长的回文子串。
代码
/**
7. @param {string} s
8. @return {string}
*/
const longestPalindrome = function (s) {
const dp = [];
for (let i = 0; i < s.length; i += 1) {
dp[i] = [];
}
let max = -1; let str = '';
for (let l = 0; l < s.length; l += 1) {
// l为所遍历的子串长度 - 1,即左下标到右下标的长度
for (let i = 0; i + l < s.length; i += 1) {
const j = i + l;
// i为子串开始的左下标,j为子串开始的右下标
if (l === 0) {
// 当子串长度为1时,必定是回文子串
dp[i][j] = true;
} else if (l <= 2) {
// 长度为2或3时,首尾字符相同则是回文子串
if (s[i] === s[j]) {
dp[i][j] = true;
} else {
dp[i][j] = false;
}
} else {
// 长度大于3时,若首尾字符相同且去掉首尾之后的子串仍为回文,则为回文子串
if ((s[i] === s[j]) && dp[i + 1][j - 1]) {
dp[i][j] = true;
} else {
dp[i][j] = false;
}
}
if (dp[i][j] && l > max) {
max = l;
str = s.substring(i, j + 1);
}
}
}
return str;
};
方法一:中心扩展
思路
回文子串一定是对称的,所以我们可以每次选择一个中心,然后从中心向两边扩展判断左右字符是否相等。
中心点的选取有两种情况:
当长度为奇数时,以单个字符为中心;
当长度为偶数时,以两个字符之间的空隙为中心。
详解
- 循环遍历字符串取得不同长度的子串;
- 通过定义好的中心扩展方法,选取奇数对称和偶数对称的中心;
- 通过比较选择出两种组合较大的回文子串长度,然后,对比之前的长度,判断是否更新起止位置;
- 全部遍历完成后,根据最后的起止位置的值,截取最长回文子串。
const longestPalindrome = function (s) {
if (s === null || s.length < 1){
return '';
}
let start = 0;let end = 0;
//从中心向两边扩展
const expandFromCenter = (s,left,right) {
while (left >= 0 && right < s.length && s[left] === s[right]) {
left -= 1;
right += 1;
}
return right - left - 1;
};
for (let i = 0;i < s.length;i += 1) {
//中心的两种选取(奇对称和偶对称)
const len1 = expandFromCenter(s,i,j);
const len2 = expandFromCenter(s,i,i+1);
//两种组合取最大的回文子串长度
const len = Math.max(len1,len2);
//如果此位置为中心的回文数长度大于之前的长度,则进行处理
if(len > end - start){
start = i - Math.floor((len - 1)/2);
end = i + Math.floor(len/2);
}
}
return s.substring(start,end + 1);
}
参考博客: 【面试助力,算法101】https://101.zoo.team/zi-fu-chuan/zi-fu-chuan-part-3-yan-zheng-hui-wen-zi-fu-chuan-shi-xian-strstr-zui-chang-gong-gong-qian-zhui-he-zu