字符串双指针用两个指针来定位一个子数组,其中一个指针指向数组的第1个数字,另一个指针指向数组的最初一个数字,那么两个指针之间所蕴含的就是一个子数组。
能够在挪动这两个指针的同时,统计两个指针之间的字符串中字符呈现的次数,这样能够解决很多常见的面试题,如在一个字符串中定位另一个字符串的变位词等。
因为这种类型的面试题都与统计字母呈现的次数无关,咱们常常应用哈希表来存储每个元素呈现的次数,因而解决这种类型的面试题通常须要同时应用双指针和哈希表。
字符串中的变位词题目:输出字符串s1和s2,如何判断字符串s2中是否蕴含字符串s1的某个变位词?如果字符串s2中蕴含字符串s1的某个变位词,则字符串s1至多有一个变位词是字符串s2的子字符串。假如两个字符串中只蕴含英文小写字母。例如,字符串s1为"ac",字符串s2为"dgcaf",因为字符串s2中蕴含字符串s1的变位词"ca",因而输入为true。如果字符串s1为"ab",字符串s2为"dgcaf",则输入为false。
由变位词的定义可知,变位词具备以下几个特点。首先,一组变位词的长度肯定雷同;其次,组成变位词的字母汇合肯定雷同,并且每个字母呈现的次数也雷同。
/** * @param {string} s1 * @param {string} s2 * @return {boolean} */var checkInclusion = function(s1, s2) { if(s2.length < s1.length) { return false; } var areAllZero = function(counts) { for(let i of counts) { if(i !== 0) { return false } } return true; } const counts = new Array(26).fill(0) for(let i = 0; i < s1.length; i++) { counts[s1.charCodeAt(i) - 97]++; counts[s2.charCodeAt(i) - 97]--; } if(areAllZero(counts)) { return true; } for (let i = s1.length; i < s2.length; ++i) { counts[s2.charCodeAt(i) - 97]-- counts[s2.charCodeAt(i - s1.length) - 97]++ if(areAllZero(counts)) { return true } } return false;};字符串中的所有变位词题目:输出字符串s1和s2,如何找出字符串s2的所有变位词在字符串s1中的起始下标?假如两个字符串中只蕴含英文小写字母。例如,字符串s1为"cbadabacg",字符串s2为"abc",字符串s2的两个变位词"cba"和"bac"是字符串s1中的子字符串,输入它们在字符串s1中的起始下标0和5。/** * @param {string} s * @param {string} p * @return {number[]} */var findAnagrams = function(s1, s2) { if(s2.length > s1.length) { return []; } let result = []; const counts = new Array(26).fill(0); var areAllZero = function(counts) { for(let i of counts) { if(i !== 0) { return false } } return true; } for(let i = 0; i < s2.length; i++){ counts[s2.charCodeAt(i)-97]++ counts[s1.charCodeAt(i)-97]-- } if(areAllZero(counts)) { result.push(0) } for(let i = s2.length; i < s1.length; i++) { counts[s1.charCodeAt(i)-97]-- counts[s1.charCodeAt(i-s2.length)-97]++ if(areAllZero(counts)) { result.push(i-s2.length+1) } } return result;};不含反复字符的最长子字符串题目:输出一个字符串,求该字符串中不含反复字符的最长子字符串的长度。例如,输出字符串"babcca",其最长的不含反复字符的子字符串是"abc",长度为3。/** * @param {string} s * @return {number} */var lengthOfLongestSubstring = function(s) { if(s.length == 0) return 0 if(s.length == 1) return 1 const map = new Map(); let max = 0 // 双指针 let left = 0 let right = 0 var areAllZero = function(counts) { for(let i of counts) { if(i[1] === 2) { return false } } return true; } while(right < s.length) { if(map.get(s.charAt(right)) !== 1) { map.set(s.charAt(right), (map.get(s.charAt(right)) || 0) + 1) right++ if(map.get(s.charAt(right)) === 1) { max = Math.max(right - left, max) } } else { map.set(s.charAt(left), (map.get(s.charAt(left)) || 0) - 1) left++ } } max = Math.max(right - left, max) return max;};蕴含所有字符的最短字符串输出两个字符串s和t,请找出字符串s中蕴含字符串t的所有字符的最短子字符串。例如,输出的字符串s为"ADDBANCAD",字符串t为"ABC",则字符串s中蕴含字符'A'、'B'和'C'的最短子字符串是"BANC"。如果不存在符合条件的子字符串,则返回空字符串""。如果存在多个符合条件的子字符串,则返回任意一个。var minWindow = (s, t) => { const charToCount = new Map(); for(let ch of t) { charToCount.set(ch, (charToCount.get(ch) || 0) + 1) } let count = charToCount.size; let start = 0, end = 0, minStart = 0, minEnd = 0; let minLength = Infinity; while(end < s.length || (count == 0) && end == s.length) { if(count > 0) { let endCh = s.charAt(end); if(charToCount.get(endCh)!== undefined) { charToCount.set(endCh, charToCount.get(endCh)-1) if (charToCount.get(endCh) === 0) { count-- } } } else { if (end - start < minLength) { minLength = end - start minStart = start minEnd = end } let startCh = s.charAt(start); if(charToCount.get(startCh)!==undefined) { charToCount.set(startCh, charToCount.get(startCh) + 1) if(charToCount.get(startCh) == 1) { count++ } } start++ } } return minLength < Infinity ? s.substring(minStrat, minEnd) : "";}上述代码中只有一个while循环,用来把两个变量从0减少到字符串s的长度。如果字符串的长度是n,那么工夫复杂度就是O(n)。能够应用一个哈希表来统计每个字符呈现的次数。哈希表的键为字符,假如字符串中只有英文字母,那么哈希表的大小不会超过256,辅助空间的大小不会随着字符串长度的减少而减少,因而空间复杂度是O(1)。
...