给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。换句话说,第一个字符串的排列之一是第二个字符串的子串。
- 初始思路:列出s1全排列(递归),判断是否是s2子串,超时
只展示全排列部分,判断是否是子串用includes方法+循环
/*递归思路:
*例如输入字符串'bcd',先取出b,然后是c,直到最后一位d,将c插入0d0的
*为0的位置,得到cd、dc;将b插入0c0d0、0d0c0为0的位置,得到bcd、cbd、
*cdb和bdc、dbc、dcb;以此类推。显然,字符串一旦长度过长,就完蛋
*/
function recursion(s1){
if(s1.length==1){ // 当输入字符串或者剩余字符串只有一个字母时,返回该字母
return [s1];
}else{
let left=s1[0]; // 取出s1左端字符,作为待插入字符
let rest=s1.slice(1,s1.length); // 取出剩余字符串
let arr=[];
var result=recursion(rest); //递归
for(let i=0;i<result.length;i++){
for(let j=0;j<=result[i].length;j++){
// 插入字符left到各个为0(空)位置
let per=result[i].slice(0,j)+left+result[i].slice(j);
arr.push(per);
}
}
return arr;
}
同时还学到一个简易的数组去重的方法(es6的set对象类似数组,但成员唯一,不会有重复的值):
let ar=Array.from(new Set(arr)); // arr为待去重字符串
踩坑的点:
- 牵扯到字符串的输入时,记得判空,length==0即可。也就是说空数组也有length属性
- 使用length属性时一定要注意是否运行到某阶段时其对象已经失去了length属性导致报错:“length” undefined
- 改进思路:滑动窗口,以s1长度固定窗口,以窗口形式在s2上从0到末尾滑动,相比于之前代码及其简洁
其实,比较是否某种排列是s2子串不需要得出s1全排列,只要比较s2子串是否含有s1里的所有字符,与顺序无关,所以用sort排序比较是否相等
var checkInclusion = function(s1, s2) {
let len = s1.length; // 固定窗口长度
let sort1 = s1.split("").sort().toString(); // 排序
for (let right = 0; right <= s2.length - len; right++) {
let subStr = s2.substr(right, len); // s1窗口滑过的s2部分
if (sort1 === subStr.split("").sort().toString()) {
// 比较窗口与s2该部分是否对应
return true;
}
}
return false;
};