力扣刷题笔记
力扣每日一题1.7将x减到0的最小操作数
力扣每日一题1.8统计包含给定前缀的字符串
前言
力扣每日一题(将x减到0的最小操作数),从题目分析到各种解决办法
题目:
给你一个整数数组nums和一个整数x。每一次操作时,你应当移除数组最左边或最右边的元素,然后从x中减去该元素的值。请注意,需要修改数组以供接下来的操作使用。
如果可以将x恰好减为0,返回最小操作数;否则,返回-1。
一、题目分析
本题主要就是在减的过程中做出选择,到底是选择数组最左边的元素还是最右边的元素,并判断是否可以减为0,因此其实可以把其抽象成二叉树的数据结构,即到底是选左边还是选右边。并最后比较每一个减为0的操作数,选出最小的操作数并返回。
二、解题方法
method1:dfs深度优先遍历,
此算法时间复杂度爆炸,在最坏情况下,即减去数组里的所有元素才可为0的情况,算法时间复杂度达到O(2^n),达到指数复杂度(因为每次可以有两个选择,需要选择n次,且后一次是在前一次的基础上做选择)因此该算法时间复杂度为指数级,会超过时间限制。
代码如下(示例):
class Solution {
private:
int minOperation = INT_MAX;
public:
void dfs(vector<int>& nums,int leftPtr,int rightPtr,int finalX,int operation){
// cout << "leftPtr: " << leftPtr << " rightPtr: " << rightPtr << " finalX: " << finalX << endl;
if(finalX < 0){
return;
}
if(leftPtr > rightPtr){
if(finalX == 0){
minOperation = min(minOperation,operation);
}
return;
}
if(finalX == 0){
minOperation = min(minOperation,operation);
return;
}
dfs(nums,leftPtr+1,rightPtr,finalX - nums[leftPtr],operation+1);
dfs(nums,leftPtr,rightPtr-1,finalX - nums[rightPtr],operation+1);
}
int minOperations(vector<int>& nums, int x) {
dfs(nums,0,nums.size()-1,x,0);
if(minOperation == INT_MAX){
return -1;
}
return minOperation;
}
};
method2:滑动窗口
本题其实可以理解为该数组的某一个前缀和某一个后缀元素之和为x,取操作数最少。
与method1一样,标记两个指针下标,leftPtr和rightPtr,leftPtr代表删除下标小于等于leftPtr的元素值,即取下标小于等于leftPtr的数组元素前缀;rightPtr代表删除下标大于等于rightPtr的元素,即取下标大于等于rightPtr的元素为后缀。
注意初条件的设置,初始时leftPtr为-1,rightPtr为0,即数组全部元素作为后缀,取和。
- 若初始条件的和小于x,即无论怎么样都不可能满足题目要求,返回-1;
- 若初始条件的和等于x,则只有全部删除元素(从前往后或从后往前删操作数一样),才可以满足题目要求
- 若初始条件的和大于x,则移动rightPtr,可减少后缀的和。
- 最后再在外部包一个循环,即移动leftPtr,增加前缀和,存储前缀和+后缀和 = x的最小操作数。
注意:在本代码中未详细规定leftPtr<=rightPtr(逻辑上应该是要规定的),但其实隐含着leftPtr<=rightPtr,因为若能进循环代表数组之和一定大于等于x,此时若leftPtr > rightPtr,则前缀和+后缀和一定大于x(有重复元素),因此rightptr++,直到rightPtr>=leftPtr才可继续循环下去
代码如下(示例):
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int size = nums.size();
int res = size + 1;
int leftPtr = -1,rightPtr = 0,leftSum = 0,rightSum = 0;
for(int i = 0;i < size;i++){
rightSum += nums[i];
}
if(rightSum < x){
return -1;
}
for(;leftPtr < size;leftPtr++){
if(leftPtr != -1){
leftSum += nums[leftPtr];
}
while(leftSum + rightSum > x && rightPtr < size){
rightSum -= nums[rightPtr];
//cout << "jhaskdk" << leftPtr << " " << rightPtr << endl;
rightPtr++;
}
if(leftSum + rightSum == x){
res = min(res,leftPtr + 1 + size - rightPtr);
}
}
return res == size + 1? -1 : res;
}
};
该方法的时间复杂度为0(n),leftPtr由0遍历到size-1,空间复杂度为O(1)
每日一题1.8(统计包含给定前缀的字符串easy)
题目描述:
给定一个字符串数组words和一个字符串pref。返回words中以pref作为前缀的字符串的数目,字符串s的前缀就是s的任一前缀连续字符串。
暴力解决即可:
class Solution {
public:
bool isPrefString(string word,string pref){
int size1 = word.size();
int size2 = pref.size();
if(size1 < size2){
return false;
}
for(int i = 0;i < size2;i++){
if(word[i] != pref[i]){
return false;
}
}
return true;
}
int prefixCount(vector<string>& words, string pref) {
int res = 0;
for(string word : words){
if(isPrefString(word,pref)){
res++;
}
}
return res;
}
};
总结
本题是用滑动窗口解决的题目,滑动窗口太久没做,不太熟悉,后面需要进一步巩固加强。(method1太傻)