中等
使用方法
无重复字符的最长字串—滑动窗口
给定一个字符串s
,找出其中不含有重复字符的最长字串的长度。(连续的)
int lengthOfLongestSubstring(char * s){
int res = 0;
int len = strlen(s);
/* 存储 ASCII 字符在子串中出现的次数 */
int freq[256] = {0};
/* 定义滑动窗口为 s[l...r] */
int l = 0, r = -1;
while (l < len) {
/* freq 中不存在该字符,右边界右移,并将该字符出现的次数记录在 freq 中 */
if (r < len - 1 && freq[s[r + 1]] == 0) {
freq[s[++r]]++;
/* 右边界无法拓展,左边界右移,刨除重复元素,并将此时左边界对应的字符出现的次数在 freq 的记录中减一 */
} else {
freq[s[l++]]--;
}
/* 当前子串的长度和已找到的最长子串的长度取最大值 */
res = fmax(res, r - l + 1);
}
return res;
}
- 滑动窗口,“窗口”内存的数子串不一定是按照原字符串那样存
- strlen(str),求字符串的长度
- 字符作为数组下标,可
- fmax(a, b),求出两个数的最大值
正则匹配—动态规划
注意,’‘匹配的意思是:前面的那个字母被匹配的次数是0次或多次,而不是自己匹配的,要与前面的一个字母一起匹配。’.*’可以匹配任意字符串
使用动态规划,定义一个状态存储器dp[][],dp[i][j]表示的是s[i]与p[j]的匹配情况,与其前面的匹配情况有关。再定义状态转移方程。m为s的长度,n为p的长度,则sp[m][n]表示最终的匹配结果。
此外,为了考虑边界情况,令dp的规模为[m+1][n+1],设dp[0][0]为true,即两个长度为0时是匹配的,然后按照上图给dp赋值时,都要给dp的i与j加一,最后dp[m+1][n+1]表示最后结果。 又,单独考虑dp的第零行的情况,如果p[j]==’‘,则dp[0][j+1]=dp[0][j-1],就考虑成,s为空时,p有字母与’‘,然后 *将其前的字母匹配0次。
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var isMatch = function(s, p) {
// 动态规划
var m = s.length, n = p.length;
var dp = [];
for (let i in [...Array(m+1).keys()]){
dp[i] = [];
for(let j in [...Array(n+1).keys()]){
dp[i][j] = false;
}
}
dp[0][0] = true;
for (let j = 0; j < n; j++){
if(p[j] === '*'){
dp[0][j+1] = dp[0][j-1];
}
}
for (let i = 0; i< m; i++){
for(let j = 0; j < n; j++){
if(s[i] === p[j] || p[j] === '.'){
dp[i+1][j+1] = dp[i][j];
}else if(s[i] !== p[j] && p[j] != '*'){
dp[i+1][j+1] = false;
}else if(p[j] === '*'){
if(s[i] === p[j-1] || p[j-1] === '.'){
dp[i+1][j+1] = dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1];
}else {
dp[i+1][j+1] = dp[i+1][j-1];
}
}
}
}
return dp[m][n]; // 最后是在上面跳出循环的结果
};
盛最多水的容器—双指针
容积就是 两个数之间的距离 乘以 两者间较小的数值。
我自己写的代码:5%
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let ob = [], n = height.length, maxS = 0, tmp = 0;
for (let i = 0; i < n; i++){
ob[i] = {};
ob[i].ind = i;
ob[i].val = height[i];
}
ob.sort((x, y) => {
return y.val - x.val;
});
for (let i = 0 ; i < n-1; i++){
let ran = Math.pow(n, 0.6);
for (let j = i+1; j<n; j++){
if(ran <= 0) break;
tmp = (Math.abs(ob[i].ind - ob[j].ind)) * ob[j].val;
if(tmp > maxS){
maxS = tmp;
}else ran--;
}
}
return maxS;
};
解析的,用双指针,一个从数组左、一个从数组右开始,每次计算两个的容积,再向中间移动较小值处的指针。
三数之和 — 双指针
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
如:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
我想的是,如下:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
var results = [], flag = 0, rn = 0;
nums = nums.sort(sortNumber);
// 先排序,再去除多余的重复数字
for (var k = 0; k < nums.length-1; k++) {
flag = 0;
for (var m = k+1; m < nums.length; m++){
if (nums[k] == nums[m]) {
flag++;
}else {
if (flag > 2 && nums[k] == 0){
nums.splice(k+3, flag-2);flag = 0;
break;
}else if (flag > 1 && nums[k] != 0){
nums.splice(k+2, flag-1);flag = 0;
break;
}
}
}
// nums数组的最后有一些重复的数字
if (m == nums.length && flag > 1) {
if (nums[k] == 0 && flag > 2) {
nums.splice(k+3, flag-2);
}else if(nums[k] != 0) {
nums.splice(k+2, flag-1);
}
}
}
// 收集三元组
for (var k = 0; k < nums.length-2 && nums[k] <= 0; k++){
for (var i = k+1; i < nums.length-1; i++) {
for (var j = i+1; j < nums.length; j++) {
if (nums[i] + nums[k] + nums[j] === 0){
results[rn++] = [nums[k], nums[i], nums[j]].sort(sortNumber);
break;
}
}
}
}
results = results.sort(sortNumber);
// 去除掉重复的三元组
for (i = 0; i < results.length-1; i++) {
for (j = i+1; j < results.length; j++) {
flag = 0;
for (k = 0; k < 3; k++){
if (results[i][k] == results[j][k]){
flag++;
}
}
if(flag == 3) {
results.splice(j,1);
j--; // 为什么不加这个就不能去掉最后一个子数组呢?
}
}
}
return results;
};
// 针对负数的排序
function sortNumber(a, b) {
return a - b;
}
最接近的三数之和 — 双指针
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。3<=n<=1000。
返回这三个数的和。
假定每组输入只存在恰好一个解。
我自己也想到了双指针,但是写的时候前面还好,中间有一个判断的地方没想到,想复杂了。下面是查看方法后改的,只在我写的基础上改了中间的if-else if -else
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function(nums, target) {
if(nums.length < 3) return;
var res = nums[0] + nums[1] + nums[2], n = nums.length;
nums = nums.sort(sortNumber); // 排序
for (var i = 0; i < n-2; i++){
var j = i+1, p = n-1;
while(j < p){
var tmp = nums[i] + nums[j] + nums[p];
var tmp_res = Math.abs(tmp-target);
if(tmp_res < Math.abs(res - target)){
res = tmp;
}
if(tmp > target)
p--;
else if(tmp < target)
j++;
else
return target
}
}
return res;
};
function sortNumber(a, b) {
return a - b;
}
时间复杂度 O(n2)
找规则
Z字形变换
实际是N字型变换,给定一个字符串s
和一个行数numRows
,以从上往下、从左往右进行N字形排列。
比如输入字符串PAYPALISHIRING
行数为3,则排列如下:
P A H N
A P L S I I G
Y I R
则对应输出为PAHNAPLSIIGYIR
,按每行输出。
实际是找字符串s中的每个字符的下标与排列中位置所在的行的关系
查看的一个解说才写出的代码:
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function(s, numRows) {
let strings = [[]], f = 2*numRows-2;
if(numRows == 0) return '';
if(numRows == 1) return s;
for (let i = 0; i < s.length; i++){
let tmp = i % f;
if(i < numRows){
strings[i] = s[i];
}else {
if(tmp < numRows){
strings[tmp] += s[i];
} else {
strings[Math.abs(f-tmp)] += s[i];
}
}
}
let result = '';
for (let i of strings){
result += i;
}
return result;
};