198.打家劫舍
dp[i]表示,考虑到下标i处所偷的最大金额,仅仅是考虑到,并不一定取该元素。
注意只有一个元素的时候要特殊处理,先判断一下,不然会溢出。有两个元素的情况就不用处理了,因为后面初始化的时候已经取了max。
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==1) return nums[0];
vector<int> dp(nums.size(),0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[nums.size()-1];
}
};
213.打家劫舍II
思路:把环形转换成线性,考虑两种情况,一是不考虑第一个元素,二是不考虑最后一个元素。
class Solution {
public:
int robrange(vector<int>& nums,int start,int end){
if(start==end) return nums[start];
vector<int> dp(nums.size()-1,0);//第一处错,不应该是end-1
dp[0]=nums[start];
dp[1]=max(nums[start],nums[start+1]);
for(int i=2;i<nums.size()-1;i++){//不应该是<end
dp[i]=max(dp[i-2]+nums[start+i],dp[i-1]);//开始写的start+2,应该是start+i
}
return dp[nums.size()-2];
}
int rob(vector<int>& nums) {
if(nums.size()==1) return nums[0];
return max(robrange(nums,0,nums.size()-2),robrange(nums,1,nums.size()-1));
}
};
这道题还是有待继续研究,改了好几次才通过,因为我的写法是dp数组的下标和nums数组的下标是不同步的,处理的细节要多一点,同步的写法可以参考卡哥的代码。
337.打家劫舍III
树形dp。用一个长度为2的一维数组即可,递归的时候系统栈里会帮我们保存每一层的参数。
class Solution {
public:
vector<int> robtree(TreeNode* cur){
if(cur==NULL) return vector<int>{0,0};
vector<int> leftdp=robtree(cur->left);
vector<int> rightdp=robtree(cur->right);
int val0=max(leftdp[0],leftdp[1])+max(rightdp[0],rightdp[1]);//左右子节点偷不偷取决于哪个是最大值
int val1=cur->val+leftdp[0]+rightdp[0];
return {val0,val1};//上面写了int了,所以就不用写vector<int>了
}
int rob(TreeNode* root) {
vector<int> result=robtree(root);
return max(result[0],result[1]);
}
};
剑指 Offer 46. 把数字翻译成字符串
思路:本题主要是递推公式。dp[i]表示到下标为i处一共有多少种翻译方法。最后两位一共有两种状态,介于0-25之间和不介于0-25之间,根据这两种情况,如果介于的话,dp[i]=dp[i-1]+dp[i-2](类似于爬楼梯),不介于的话只能当个字符进行翻译,dp[i]=dp[i-1]。
class Solution {
public:
int translateNum(int num) {
string str=to_string(num);
if(str.size()==1) return 1;
vector<long long> dp(str.size());
dp[0]=1;
if((str[0]=='1')||(str[0]=='2'&&str[1]<='5')) dp[1]=2;
else{
dp[1]=1;
}
for(int i=2;i<str.size();i++){
if((str[i-1]=='1')||(str[i-1]=='2'&&str[i]<='5')) dp[i]=dp[i-1]+dp[i-2];
else{
dp[i]=dp[i-1];
}
}
return dp[str.size()-1];
}
};
注意函数to_string的使用。
注意开始要进行判断,如果就一位的话,后面写str[1]会溢出。
注意判断条件,如果前一位是1的话,后一位是什么都无所谓,如果是2的话,后一位(当前位)只能<='5'。
48.最长不含重复字符的子字符串
思路:双指针+哈希表。这个解法还是很巧妙的。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> hash;
int res=0;
for(int i=0,j=0;j<s.size();j++){
hash[s[j]]++;
while(hash[s[j]]>1){//这个while循环相当于去重
hash[s[i]]--;
i++;//这个不能放前面,因为放前面的话,i开始指向的位置还没减呢
}
res=max(res,j-i+1);
}
return res;
}
};
好像还有滑动窗口的解法,还有待研究。