class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int len = nums.size();
vector<int> vt;
vector<int> res(len,-1);
for(int i =0 ; i< len*2 ;i++)
{
while(!vt.empty()&&nums[i%len]>nums[vt.back()])
{
res[vt.back()]=nums[i%len];
vt.pop_back();
}
vt.push_back(i%len);
}
return res;
}
};
依旧是使用mp转vector重写sort函数法
class Solution {
public:
static bool func(pair<int,int>& p1, pair<int,int>& p2)
{
return p1.first>p2.first;
}
vector<string> findRelativeRanks(vector<int>& nums) {
int len = nums.size();
unordered_map<int,int> mp;
int i=0;
for(auto num:nums)mp[num]=i++;
vector<pair<int,int>> order(mp.begin(),mp.end());
sort( order.begin(),order.end(),func);
vector<string> res(len);
for(int i=0;i<len;i++)
{
if(i==0){res[order[i].second]="Gold Medal";}
else if(i==1){res[order[i].second]="Silver Medal";}
else if(i==2){res[order[i].second]="Bronze Medal";}
else{res[order[i].second]=to_string(i+1);}
}
return res;
}
};
发现堆排序好像也很好用
利用堆来排序,建立一个优先队列,把分数和其坐标位置放入队列中,会自动按其分数高低排序,然后我们从顶端开始一个一个取出数据,由于保存了其在原数组的位置,我们可以直接将其存到结果res中正确的位置,用一个变量cnt来记录名词,前三名给奖牌,后面就是名次数。
class Solution {
public:
vector<string> findRelativeRanks(vector<int>& nums) {
int n = nums.size(), cnt = 1;
vector<string> res(n, "");
priority_queue<pair<int, int>> heap;
for(int i =0; i <n; i++){
heap.push({nums[i], i});
}
for(int i = 0; i < n; i++){
int dx = heap.top().second;
heap.pop();
if(cnt == 1) res[dx] = "Gold Medal";
else if(cnt == 2) res[dx] = "Silver Medal";
else if(cnt == 3) res[dx] = "Bronze Medal";
else res[dx] = to_string(cnt);
cnt++;
}
return res;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:
vector<int> res;
public:
void dfs(vector<TreeNode*> cur)
{
if(cur.empty())return;
vector<TreeNode*> next;
res.push_back(cur[0]->val);
for(auto i:cur)
{
if(i->left)next.push_back(i->left);
if(i->right)next.push_back(i->right);
}
dfs(next);
}
int findBottomLeftValue(TreeNode* root) {
vector<TreeNode*> temp={root};
dfs(temp);
int len = res.size();
return res[len-1];
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:
vector<int> res;
public:
void dfs(vector<TreeNode*> cur)
{
if(cur.empty())return;
int m = INT_MIN;
vector<TreeNode*> next;
for(auto i:cur)
{
m = max(m,i->val);
if(i->left)next.push_back(i->left);
if(i->right)next.push_back(i->right);
}
res.push_back(m);
dfs(next);
}
vector<int> largestValues(TreeNode* root) {
if(root==NULL)return {};
vector<TreeNode*> temp={root};
dfs(temp);
return res;
}
};
总结一下:
最长公共连续子串(大一圈正常遍历)
这个必须要记住
if(s1[i]==s2[i]) v[i][j] =v[i-1]v[j-1] +1
else v[i][j] = 0
最长回文子串
把这个字符串翻过来然后用和上面一样的方法求就行了
所有的回文子串(从右下开始遍历右上部分)
其实可以从小到大进行遍历,然后也不适用这个dp表达式,而是新写一个回文函数每次进行判断,这样实用性更广
if(s1[i]==s2[j] && ( j-i<=2 || v[i+1][j-1]) v[i][j] = true;
最长公共子序列(大一圈正常遍历)
这个必须要记住
if(s1[i]==s2[j]) v[i][j] = v[i-1][j-1]+1
else v[i][j] = max(v[i-1][j],v[i][j-1])
最长回文子序列
因为是子序列不是子串,所以可以逆置原字符串,采用最长公共子序列LCS来求解。
逆置后公共子序列就是回文子序列。
class Solution {
public:
int longestPalindromeSubseq(string s) {
int len = s.size();
//vector> v(len, vector(len, false));
/*
int max = 0 ;
for(int i = len-1; i>=0 ;i--)
{
for(int j = i ; j max)max = j -i+1;
}
}
}
return max;
*/
string s1=s;
reverse(s1.begin(),s1.end());
vector<vector<int>> v(len+1, vector<int>(len+1, 0));
for(int i = 1 ; i<len+1 ;i++)
{
for(int j = 1 ; j<len+1 ; j++)
{
if(s[i-1]==s1[j-1]) v[i][j] =v[i-1][j-1]+1;
else v[i][j] = max(v[i-1][j],v[i][j-1]);
}
}
return v[len][len];
}
};
dfs超时
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(auto n:nums)sum+=n;
if(sum%2==1)return false;
//容量为sum/2,物品容量和价值都为nums
int N=nums.size();
vector<vector<int>> dp(N+1,vector<int>(sum/2+1,0));
for(int i = 1;i<=N;i++)
{
for(int j =1;j<=sum/2;j++)
{
if(j<nums[i-1]) {dp[i][j]=dp[i-1][j];}
else
{
dp[i][j]=max( dp[i-1][j], dp[i-1][j-nums[i-1]] + nums[i-1] );
}
}
}
return dp[N][sum/2]==sum/2;
}
};
dp
class Solution {
public:
int change(int amount, vector<int>& coins) {
int n = coins.size();
vector<vector<int>> dp(n + 1, vector<int>(amount + 1));
//base case:
for(int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for(int i = 1; i <= amount; i++) {
dp[0][i] = 0;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= amount; j++) {
if(j - coins[i - 1] >= 0) dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]]; //完全背包,选的情况仍为i
else dp[i][j] = dp[i - 1][j];
}
}
return dp[n][amount];
}
};
作者:Komaeda_Nagito
链接:https://leetcode-cn.com/problems/coin-change-2/solution/dp-wan-quan-bei-bao-ji-ben-zuo-fa-c-by-kiritoh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
dfs超时,推荐dfs
class Solution {
public:
//dfs的时间复杂度高,当dfs复杂度高用bfs空间复杂度高的试试
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
int len1 = matrix.size();
int len2 = matrix[0].size();
vector<vector<int>> board(len1,vector<int>(len2,0));
vector<vector<int>> visited(len1,vector<int>(len2,0));//访问数组
queue<vector<int>> qt;
for(int i = 0;i<len1;i++)
{
for(int j =0;j<len2;j++)
{
if(matrix[i][j]==0){vector<int> t;t.push_back(i);t.push_back(j);visited[i][j]=1;qt.push(t);}
}
}
int num = 1;
while(!qt.empty())
{
int len = qt.size();
for(int c = 0;c<len;c++)
{
auto temp = qt.front();qt.pop();
int i = temp[0],j = temp[1];
if(i-1>=0 && visited[i-1][j]==0){board[i-1][j]=num; qt.push({i-1,j});visited[i-1][j]=1;}
if(j-1>=0 && visited[i][j-1]==0){board[i][j-1]=num; qt.push({i,j-1});visited[i][j-1]=1;}
if(i+1<len1 && visited[i+1][j]==0){board[i+1][j]=num; qt.push({i+1,j});visited[i+1][j]=1;}
if(j+1<len2 && visited[i][j+1]==0){board[i][j+1]=num; qt.push({i,j+1});visited[i][j+1]=1;}
}
num++;
}
return board;
}
};
在应用题中的dfs经常会遇到一个问题,在查看这个点四周的状态时,下一次递归又会回到这个地方,容易造成无限循环,在这道题中,可以使用不传入引用值,将已经搜索过的数改为其他数的情况,但这种方法并不万能
这里面值得推荐的一个地方时,利用递归的次数来作为要返回的数量
class Solution {
private:
int m;
int n;
public:
int dfs(int i, int j, vector<vector<int>> matrix)
{
if (i < 0 || i >= m || j < 0 || j >= n) return 20000;
if (matrix[i][j] == 0)return 0;
if (matrix[i][j] == 'A')return 20000;
matrix[i][j] = 'A';
int num1 = min(dfs(i - 1, j, matrix),dfs(i+1,j,matrix));
int num2 = min(dfs(i, j + 1, matrix), dfs(i, j - 1, matrix));
return min(num1,num2)+1;
}
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
if (matrix.empty()) return matrix;
m = matrix.size();
n = matrix[0].size();
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
matrix[i][j] = dfs(i, j, matrix);
}
}
return matrix;
}
};
动态规划方法,对于有些问题,很好找到其与旁边的对应关系
解题思路
定义dp[i][j]表示该位置距离0的最短距离,其实很容易想到dp[i][j]要么等于0,要么等于min(dp[i-1][j],dp[i+1][j],dp[i][j-1],dp[i][j+1])+1
这个问题棘手的就是,我们更新状态的时候,要么从左上到右下,要么右下到左上,或者更不常见的右上到左下以及左下到右上。无论哪种更新方式都只能依赖两个前置状态(比如从左上到右下时, dp[i][j]只能依赖dp[i-1][j]和dp[i][j-1])。
这里做两遍dp状态的更新来解决上述问题, 第一次从左上到右下,转移方程为:
dp[i][j] = 0 或
dp[i][j] = min(dp[i][j-1]+1, dp[i-1][j]+1)
第二次更新从右下到左上,转移方程为
dp[i][j] = 0 或
dp[i][j] = min(dp[i][j], dp[i][j+1]+1, dp[i+1][j]+1)
齐活
作者:yuexiwen
链接:https://leetcode-cn.com/problems/01-matrix/solution/c-2bian-dp-jian-dan-yi-dong-by-yuexiwen/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
vector<vector<int>> dp(matrix.size(), vector<int>(matrix[0].size(), 0));
for (int i = 0; i < matrix.size(); ++i) {
for (int j = 0; j < matrix[0].size(); ++j) {
if (!matrix[i][j]) continue;
int t_val = i == 0 ? 10001 : dp[i-1][j] + 1;
int l_val = j == 0 ? 10001 : dp[i][j-1] + 1;
dp[i][j] = min(t_val, l_val);
}
}
for (int i = matrix.size()-1; i >= 0; --i) {
for (int j = matrix[0].size()-1; j >= 0; --j) {
if (!matrix[i][j]) continue;
int b_val = i == matrix.size()-1 ? 10001 : dp[i+1][j] + 1;
int r_val = j == matrix[0].size()-1 ? 10001 : dp[i][j+1] +1;
dp[i][j] = min(dp[i][j], min(b_val, r_val));
}
}
return dp;
}
};
作者:yuexiwen
链接:https://leetcode-cn.com/problems/01-matrix/solution/c-2bian-dp-jian-dan-yi-dong-by-yuexiwen/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
只要对每一个结点求出其左右子树深度之和,这个值作为一个候选值,然后再对左右子结点分别调用求直径对递归函数,这三个值相互比较,取最大的值更新结果res,因为直径不一定会经过根结点,所以才要对左右子结点再分别算一次。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:
int res = 0;
public:
int maxDepth(TreeNode* root)
{
if(root==NULL)return 0;
return max( maxDepth( root->left),maxDepth( root->right) )+1;
}
void maxValue(TreeNode* root)
{
if(root==NULL)return;
res = max(res, maxDepth(root->left)+maxDepth(root->right) );
maxValue(root->left);
maxValue(root->right);
}
int diameterOfBinaryTree(TreeNode* root) {
maxValue(root);
return res;
}
};
void init() {
for (int i = 1; i <= n; ++i) {
father[i] = i;
}
}
int get(int x) {
if (father[x] == x) { // x 结点就是根结点
return x;
}
return get(father[x]); // 返回父结点的根结点
}
void merge(int x, int y) {
x = get(x);
y = get(y);
if (x != y) { // 不在同一个集合
father[y] = x;
}
}
路径压缩
int get(int x) {
if (father[x] == x) { // x 结点就是根结点
return x;
}
return father[x] = get(father[x]); // 返回父结点的根结点,并另当前结点父结点直接为根结点
}
————————————————
版权声明:本文为CSDN博主「zjy_code」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zjy_code/article/details/80634149
不管是大侠问题还是朋友圈问题,这类问题都用并查集模板来实现,本来想维护一个set,但是很容易陷到无限循环的陷阱中
并查集模板
struct DSU
{
std::vector<int> data;
void init(int n) { data.assign(n, -1); }
bool unionSet(int x, int y)
{
x = root(x);
y = root(y);
if (x != y)
{
if (data[y] < data[x])
{
std::swap(x, y);
}
data[x] += data[y];
data[y] = x;
}
return x != y;
}
bool same(int x, int y) { return root(x) == root(y); }
int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
int size(int x) { return -data[root(x)]; }
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& M)
{
int ans = M.size();
DSU dsu;
dsu.init(M.size());
for (size_t i = 0; i < M.size(); i++)
{
for (size_t j = i + 1; j < M.size(); j++)
{
if (M[i][j] == 0) continue;
ans -= dsu.unionSet(i, j);
}
}
return ans;
}
};
作者:ikaruga
链接:https://leetcode-cn.com/problems/friend-circles/solution/547-by-ikaruga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:ikaruga
链接:https://leetcode-cn.com/problems/friend-circles/solution/547-by-ikaruga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
很好的解答
/*
参考思想如下:https://blog.csdn.net/qq_41593380/article/details/81146850
参考了两份C++实现,改进以后形成以下代码
*/
class Solution {
public:
int father[210];
//查找祖先节点,当节点记录的祖先是自己,则表示查找到祖先了
int findFather(int x)
{
while(x!=father[x])
{
x = father[x];
}
return x;
}
//合并节点:设置共同祖先
void Union(int a,int b)
{
int fa = findFather(a);
int fb = findFather(b);
if(fa!=fb)
{
father[fa] = fb;
}
}
//最开始的时候,每个节点时分散的,都是自己的祖先
void init()
{
for(int i=0;i<210;i++)
{
father[i] = i;
}
}
//主函数
int findCircleNum(vector<vector<int>>& M) {
init();
//对N个学生两两做判断
for(int i=0;i<M.size();i++)
{
for(int j=i+1;j<M.size();j++) //只遍历少半个矩阵
{
if(M[i][j]==1)
{
Union(i,j); //对有关系的进行合并,使其为同一个父节点
}
}
}
//一次遍历找到所有祖先节点,即为朋友圈的个数
int res = 0;
for(int i=0;i<M.size();i++)
{
if(i==father[i])
{
res++;
}
}
return res;
}
};
作者:zhang-zhe
链接:https://leetcode-cn.com/problems/friend-circles/solution/c-bing-cha-ji-shi-xian-by-zhang-zhe/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这道题用哈希实现是太简单了,用新的数组不仅复杂还容易出错。不要用数组去储存前n项和,而是储存在哈希表里,因为这样还能保存次数。虽然子串要求顺序,,但是这和就是连续求的并不影响。
用一个哈希表来建立连续子数组之和跟其出现次数之间的映射,初始化要加入{0,1}这对映射,这是为啥呢,因为我们的解题思路是遍历数组中的数字,用sum来记录到当前位置的累加和,我们建立哈希表的目的是为了让我们可以快速的查找sum-k是否存在,即是否有连续子数组的和为sum-k,如果存在的话,那么和为k的子数组一定也存在,这样当sum刚好为k的时候,那么数组从起始到当前位置的这段子数组的和就是k,满足题意,如果哈希表中事先没有m[0]项的话,这个符合题意的结果就无法累加到结果res中,这就是初始化的用途。
————————————————
版权声明:本文为CSDN博主「pushup8」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pushup8/article/details/85341207
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int sum = 0, ans = 0;
unordered_map<int,int> mp;
mp[0] = 1;
for(int i: nums){
sum += i;
if(mp.find(sum-k) != mp.end()) ans += mp[sum-k];
mp[sum] ++;
}
return ans;
}
};
作者:jarvis1890
链接:https://leetcode-cn.com/problems/subarray-sum-equals-k/solution/qian-zhui-he-shi-xian-jian-dan-by-jarvis1890/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
bool checkInclusion(string s1, string s2) {
unordered_map<char,int> mp1,window;
for(auto s:s1)mp1[s]++;
for(int i = 0;i< s2.size() ;i++)
{
if(i<s1.size())
{window[s2[i]]++;}
else
{
window[s2[i]]++;
if(window[s2[i-s1.size()]]>1)
{window[s2[i-s1.size()]]--;}
else window.erase(s2[i-s1.size()]);
}
if(window==mp1)return true;
}
return false;
}
};
里面有两个值得注意的地方,一个是使用||优化单个节点位空的情况,一个是主递归里必须判断s是否为0,虽然到负递归里不会出错,但是主递归里涉及到了s的子节点所以必须判断
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isequal(TreeNode* root1,TreeNode* root2)
{
if(root1==NULL && root2==NULL)return true;
//if(root1!= NULL && root2==NULL)return false;
//if(root1==NULL && root2!=NULL)return false;
if(root1==NULL || root2==NULL)return false;
if(root1->val != root2->val)return false;
return isequal(root1->left,root2->left) && isequal(root1->right,root2->right);
}
bool isSubtree(TreeNode* s, TreeNode* t) {
if(s==NULL)return false;
if( isequal(s,t)==true )return true;
return isSubtree(s->left,t) || isSubtree(s->right,t);
}
};
超时,但是道理时正确的
class Solution {
int res;
public:
void dfs(int m, int n, int N,vector<vector<int>> M, int i, int j,int num)
{
if(num>N)return;//当步数已经大于N就失效了
if( (i<0 || i>=m || j<0 || j>= n) && num<=N ) {res++;return;}//到达边界而且满足要求的
dfs(m,n,N,M,i+1,j,num+1);
dfs(m,n,N,M,i-1,j,num+1);
dfs(m,n,N,M,i,j+1,num+1);
dfs(m,n,N,M,i,j-1,num+1);
}
int findPaths(int m, int n, int N, int i, int j) {
vector<vector<int>> M(m, vector<int>(n,1));
int num=0;
dfs(m,n,N,M,i,j,num);
return res;
}
};
class Solution {
public:
int minDistance(string word1, string word2) {
int len1 = word1.size();
int len2 = word2.size();
vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
for(int i =1 ;i<=len1 ;i++)
{
for(int j =1 ;j <=len2 ;j++)
{
if(word1[i-1]==word2[j-1])
{dp[i][j] = dp[i-1][j-1] + 1;}
else
{dp[i][j] = max(dp[i-1][j],dp[i][j-1]);}
}
}
return len1+len2-2*dp[len1][len2];
}
};
class Solution {
public:
int findLHS(vector<int>& nums) {
unordered_map<int,int> mp;
for(auto num:nums)
{
if(mp.find(num)!=mp.end())mp[num]++;
else mp[num]=1;
}
int res=0;
set<int> st(nums.begin(),nums.end());
for(auto num:st)
{
if(mp.find(num-1)!=mp.end())
{res = max(res,mp[num]+mp[num-1]);}
if(mp.find(num+1)!=mp.end())
{res= max(res, mp[num]+mp[num+1]);}
}
return res;
}
};
边界条件态麻烦这样做
在原序列左右两侧都先添个0
class Solution {
public:
bool canPlaceFlowers(vector<int>& flowerbed, int n) {
int res = 0;
if(flowerbed.empty())return false;
if((int)flowerbed.size()==1)return !flowerbed[0] || !n;
for(int i = 0; i< (int)flowerbed.size() ;i++)
{
if(i==0){if(flowerbed[i]==0 && flowerbed[i+1]==0){flowerbed[i]=1;res++;}}
else if (i==(int)flowerbed.size()-1){if(flowerbed[i]==0 && flowerbed[i-1]==0){flowerbed[i]=1;res++;}}
else {if(flowerbed[i]==0 && flowerbed[i-1]==0 && flowerbed[i+1]==0) {flowerbed[i]=1;res++;}}
}
return res>=n;
}
};
一遍遍历的方法,但是还是超时了
class Solution {
public:
vector<int> findErrorNums(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<int> ref; int res1 = -1, res2 = -1;
for(auto i:nums)
{
if(find(ref.begin(),ref.end(),i)!=ref.end())res1 = i;
if(!ref.empty() && ref.back()<i-1)res2 = i-1;
ref.push_back(i);
}
if(res2==-1)
{
if(nums[0]!=1)res2 = 1;
else res2 = nums.size();
}
return {res1,res2};
}
};
使用哈希表的方法,注意一般不对哈希表进行遍历,因为迭代器不支持加减操作十分难用,只用其查找操作
class Solution {
public:
vector<int> findErrorNums(vector<int>& nums) {
sort(nums.begin(),nums.end());
unordered_map<int,int> mp;
int res1 = -1,res2 = -1;
for(auto i:nums)
{
mp[i]++;if(mp[i]==2)res1 = i;
}
for(int i=1;i<=nums.size();i++)
{
if(mp[i]==0){res2= i;break;}
}
return {res1,res2};
}
};
两次遍历,就是使用find,超时了,可以看到vec中的find复杂度还是很高的
class Solution {
public:
vector<int> findErrorNums(vector<int>& nums) {
int res1 = 0,res2 = 0;
for(int i =0;i<nums.size();i++)
{
if( find(nums.begin(),nums.begin()+i,nums[i])!=nums.begin()+i)
{res1 = nums[i];break;}
}
for(int i =1;i<=nums.size();i++)
{
if(find(nums.begin(),nums.end(),i)==nums.end())
{res2 = i;break;}
}
return {res1,res2};
}
};
class Solution {
public:
int findLongestChain(vector<vector<int>>& pairs) {
//把重合的去掉
if(pairs.empty())return 0;
sort(pairs.begin(), pairs.end());
int dp = pairs[0][1], res = 0;
for(int i = 1 ; i< (int)pairs.size() ;i++)
{
if( pairs[i][0]<= dp)
{
res++;
dp = min(dp, pairs[i][1]);
}
else
{
dp = pairs[i][1];
}
}
return (int)pairs.size()-res;
}
};
和套娃问题非常类似,唯一不一样的是需要下一个的第一个大于前一个的最后一个
注意需要排序
注意需要条件筛选
注意dp表达式
class Solution {
public:
static bool myfunc(const vector<int> num1, const vector<int> num2)
{
if( num1[0]<num2[0] || (num1[0]==num2[0] && num1[1]>num2[1] ))
{return true;}
else
{return false;}
}
int findLongestChain(vector<vector<int>>& pairs) {
int len = pairs.size();
sort(pairs.begin(),pairs.end(),myfunc);
int res = 0;
vector<int> dp(len,1);
for(int i =0 ; i<len ;i++)
{
for(int j = 0 ;j<i ;j++)
{
if(pairs[i][0]>pairs[j][1]){dp[i] = max(dp[i],dp[j]+1);}
}
res = max(res,dp[i]);
}
return res;
}
};
dp[i][j]表示[i, j]范围内字符是否为回文串,i从后向前遍历,j从i位置向后遍历,如果s[i] == s[j],那么i和j满足下面两个条件之一,则dp[i][j] = true。
如果i和j相邻或只隔着一个字符,则dp[i][j] = true
否则如果dp[i + 1][j - 1] = true,则dp[i][j] = true
回文字符串判断:使用二维的布尔矩阵
若单个字符(即对角线处)一定为回文
若相邻的相等(即对角线往右上一斜排)一定为回文
若隔一个的相等,一定为回文
上面满足的两个条件,相等且距离小于等于2
当相等且里面的为回文,那么这两个也是回文,
满足的两个条件:相等且里面两个为回文
class Solution {
public:
int countSubstrings(string s) {
if (s.empty()) return 0;
int size = s.size(), res = 0;
vector<vector<bool>> dp(size, vector<bool>(size));
for (int i = size - 1; i >= 0; --i) {
for (int j = i; j < size; ++j) {
dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1]);
if (dp[i][j]) ++res;
}
}
return res;
}
};
作者:guohaoding
链接:https://leetcode-cn.com/problems/palindromic-substrings/solution/647-hui-wen-zi-chuan-liang-chong-fang-fa-zhong-xin/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
bool flag = false;
void dfs(vector<int>& nums,int sum ,int target,vector<int>& record,int num,int k)
{
if(flag)return;
if(sum==target){num++;sum=0;}
if(num==k){flag=true;return;}
for(int i = 0;i<nums.size();i++)
{
if(record[i]==0)
{
record[i]=1;
sum += nums[i];
dfs(nums,sum,target,record,num,k);
sum -= nums[i];
record[i]=0;
}
}
}
inline static bool cmp(int a,int b)
{return b>a;}
bool canPartitionKSubsets(vector<int>& nums, int k) {
int sum = 0;
for(auto n:nums)sum+=n;
if(sum%k!=0)return false;
sort(nums.begin(),nums.end(),cmp);
int target = sum/k;
if(nums.front()>target)return false;
vector<int> record(nums.size(),0);
dfs(nums,0,target,record,0,k);
return flag;
}
};
最关键的还是边界问题,因为这道题里只要相等的值,这样的话边界一旦不满足可以直接进行范围缩小,然后在整个循环的时候,要有等号
class Solution {
public:
int search(vector<int>& nums, int target) {
int len=nums.size();
int left = 0,right = len-1;
while(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]==target)return mid;
else if(nums[mid]>target)
{
right = mid-1;
}
else
{
left = mid+1;
}
}
return -1;
}
};
求ascii直接将字符相加就行,本质上就是求公共最大子序列,但是要将dp关系式改一下,里面不再存个数,而是直接求这个字符串的ascii码之和
class Solution {
public:
int minimumDeleteSum(string s1, string s2) {
//其实就是在求最大公共子序列
int len1 = s1.size();
int len2 = s2.size();
vector<vector<int>> dp(len1+1,vector<int> (len2+1,0));
for(int i = 1 ; i<len1+1; i++)
{
for(int j =1 ; j< len2+1 ;j++)
{
if(s1[i-1]==s2[j-1]) dp[i][j] = dp[i-1][j-1] + int(s1[i-1]);
else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
//dp[i][j]为最大的长度,但是现在需要知道是哪些字符串
int sum1 = 0;
for(int i = 0 ; i< len1;i++)
{
sum1 += int(s1[i]);
}
int sum2 = 0;
for(int i = 0 ; i< len2 ; i++)
{
sum2 += int(s2[i]);
}
return sum1 +sum2 - 2*dp[len1][len2];
}
};
是求公共子串中判断回文子串类似的问题,为了防止溢出要注意剪枝
class Solution {
public:
bool is(vector<int> temp,int k)
{
int sum = temp[0];
int len = temp.size();
for(int i = 1 ; i<len ;i++ )
{
sum = sum *temp[i];
}
if(sum<k) return true;
else return false;
}
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
//采用的是二维数组法,遍历半个,每次进行判断
int res=0;
int len = nums.size();
vector<vector<int>> dp(len, vector<int>(len,0));
for(int i = 0; i<len ;i++)
{
for(int j = i ; j<len ;j++)
{
vector<int> temp(nums.begin()+i,nums.begin()+j+1);
if(is(temp,k)) res++;
else break;
}
}
return res;
}
};
要特别注意题目里是否必须是连续的,如果可以不用连续(最长公共子序列)就用注释里的方法做,如果可以连续(最长公共子串)就用另一种dp方法做
class Solution {
public:
int findLength(vector<int>& A, vector<int>& B) {
int lena = A.size();
int lenb = B.size();
vector<vector<int>> v(lena +1 , vector<int>(lenb+1,0));
/*
for(int i = 1 ; i< lena+1 ; i++)
{
for(int j = 1; j
int max = 0;
for(int i = 1 ; i< lena+1 ; i++)
{
for(int j = 1; j<lenb+1 ; j++)
{
if(A[i-1] == B[j-1]) v[i][j] = v[i-1][j-1]+1;
else v[i][j]=0;
if(v[i][j]>max)max = v[i][j];
}
}
return max;
}
};
class Solution {
public:
int monotoneIncreasingDigits(int N) {
for(int i =N; i>=0 ;i--)
{
string s = to_string(i);
int len = s.size();
bool b = true;
for(int i = 1; i<len ;i++)
{
if(s[i]<s[i-1]){b = false;break;}
}
if(b)return i;
}
return 0;
}
};
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& T) {
//第一种是暴力解法容易想到
//第二种单调栈
vector<int> st;
vector<int> res(T.size(),0);
for(int i = 0; i< T.size();i++)
{
while(!st.empty() && T[i]>T[st.back()])
{
res[st.back()]=i-st.back();
st.pop_back();
}
st.push_back(i);
}
return res;
}
};
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<vector<int> > adj(N + 1, vector<int>(N + 1, INT_MAX));/邻接矩阵
vector<int> dist(N + 1, INT_MAX);
vector<bool> visited(N + 1, false);//是否访问过
dist[K] = 0;
visited[K] = true;//初始点访问
for (auto& e : times) {
adj[e[0]][e[1]] = e[2];//放入邻接矩阵中
if (e[0] == K) {
dist[e[1]] = e[2];
}
}
for (int i = 1; i < N; ++i) {
int min_dist = INT_MAX;
int min_ind = -1;
for (int j = 1; j <= N; ++j) {
////找到未访问过的里面的最小值,
if (!visited[j] && dist[j] < min_dist) {
min_dist = dist[j];//找到最小值
min_ind = j;
}
}
if (min_dist == INT_MAX)
return -1;
visited[min_ind] = true;
for (int j = 1; j <= N; ++j) {
if (!visited[j] && adj[min_ind][j] != INT_MAX &&
min_dist + adj[min_ind][j] < dist[j])
dist[j] = min_dist + adj[min_ind][j];
}
}
int max_dist = -1;
for (int i = 1; i <= N; ++i)
if (max_dist < dist[i])
max_dist = dist[i];
return (max_dist == INT_MAX) ? -1 : max_dist;
}
};
作者:da-li-wang
链接:https://leetcode-cn.com/problems/network-delay-time/solution/c-dijkstra-by-da-li-wang/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
动态规划:找到关系式,然后进行遍历构造dp数组
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
//理论上可以用递归每次都选择走一步或者走两步,但是树会很庞大
//所谓dp,通常要求出一个表达式,这个表达式一般是用之前的量的函数构成,最后同循环遍历或者递归实现
int len = cost.size();
vector<int> dp = cost;
cost.push_back(0);
dp.push_back(0);
for(int i = 2 ; i< len+1 ; i++)
{
dp[i] = cost[i] + min(dp[i-1],dp[i-2]);
}
return dp[len];
}
};
class Solution {
public:
vector<int> partitionLabels(string S) {
int xu1=0;
int xu0=0;
vector<int> res;
for(int i=0;i<S.size();i++){
int tmp=S.find_last_of(S[i]);
if(tmp>xu1) xu1=tmp;
if(i==xu1) {
res.push_back(xu1-xu0);
xu0=xu1;
}
}
res[0]++;
return res;
}
};
作者:kalase
链接:https://leetcode-cn.com/problems/partition-labels/solution/shan-yong-findsui-ran-you-dian-man-by-kalase/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
bool isToeplitzMatrix(vector<vector<int>>& matrix) {
int l1 = matrix.size();
int l2 = matrix[0].size();
for(int i=1 ;i<l1 ;i++)
{
for(int j =1 ;j<l2 ;j++)
{
if(matrix[i][j]!=matrix[i-1][j-1])return false;
}
}
return true;
}
};
一开始是这样做的,但是发现始终不对,因为每次对pair的second修改不成功,于是去vs试了一下
class Solution {
public:
static inline bool func(const pair<char,int>& p, const pair<char,int>& q)
{
return p.second>q.second;
}
string reorganizeString(string S) {
unordered_map<char,int> mp;
for(auto i:S)mp[i]++;
vector<pair<char,int>> vt;
vt.assign(mp.begin(),mp.end());
sort(vt.begin(),vt.end());
string res;
while((int)res.size()<(int)S.size())
{
for(auto v:vt)
{
if(v.second>=1)
{
res = res + v.first;
v.second -= 1;
}
}
}
int len = res.size();
//return (res[len-1]!=res[len-2])?res:"";
return res;
}
};
下面两种对pair值修改的结果竟然是不同的,说明对pair的修改必须建立在整体索引之上,像auto v:vt这样取出再修改是不行的
vector<pair<int, int>> v = {pair<int,int>(1,2)};
pair<int,int> vt = v[0];
//vt.second--;
v[0].second--;
cout << v[0].second << endl;
class Solution {
public:
int peakIndexInMountainArray(vector<int>& A) {
int len = A.size();
int left = 0,right = len;
while(left<right)
{
int mid=left+(right-left)/2;
if(A[mid]>A[mid-1]&& A[mid]>A[mid+1])return mid;
else if(A[mid-1]<A[mid]&&A[mid]<A[mid+1])
{left=mid+1;}
else
{right=mid;}
}
return left;
}
};
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
unordered_map<int,int> mp;
for(auto bill:bills)
{
if(bill==5)mp[5]++;
else if(bill==10)
{
if(mp[5]>=1){mp[5]--;mp[10]++;}
else return false;
}
else
{
if(mp[10]>=1 && mp[5]>=1){mp[10]--;mp[5]--;}
else if(mp[5]>=3){mp[5] -=3;}
else return false;
}
}
return true;
}
};
对vector很灵活的用法
class Solution {
public:
vector<int> advantageCount(vector<int>& A, vector<int>& B) {
sort(A.begin(), A.end());
vector<int> ans;
for (int i = 0; i < B.size(); i++) {
auto iter = upper_bound(A.begin(), A.end(), B[i]);
if (iter != A.end()) {
ans.push_back(*iter);
A.erase(iter);
} else {
ans.push_back(A[0]);
A.erase(A.begin());
}
}
return ans;
}
};
作者:nimodawoha
链接:https://leetcode-cn.com/problems/advantage-shuffle/solution/er-fen-cha-zhao-pai-xu-by-nimodawoha/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路应该是找到最大的一组,然后一个一个试,但是为了更快速的,采用二分法来试,在二分法的边界问题上十分讲究,稍有不慎就会出错
class Solution {
public:
bool eat(int speed, vector<int>& piles, int H)
{
int sum = 0;
for(auto pile:piles)
{
sum +=(pile+speed-1)/speed;
}
return sum>H;
}
int minEatingSpeed(vector<int>& piles, int H) {
int len = piles.size();
int m=0;
for(auto pile:piles)
{
m =max(m,pile);
}
int left =1;
int right = m;
while(left<right)
{
int mid=(left+right)/2;
if(eat(mid,piles,H))
{
left =mid +1;
}
else
{
right =mid;
}
}
return left;
}
};
思路还有点难想,但是首位双指针法很有道理
class Solution {
public:
int numRescueBoats(vector<int>& people, int limit) {
int res = 0;
sort(people.begin(),people.end());
int i = 0, j=(int)people.size()-1;
while(i<j)
{
if(people[i]+people[j]<=limit)
{
res++;i++;j--;
}
else
{
res++;j--;
}
}
if(i==j)res++;
return res;
}
};
在折叠的时候会丢失信息,专门用一个数组储存这个丢失的个数信息
class StockSpanner {
public:
StockSpanner() {}
int next(int price) {
int ans=1;
while(!pri.empty()&&pri.top()<=price){
ans+=cache.top();
cache.pop();
pri.pop();
}
pri.push(price);
cache.push(ans);
return ans;
}
private:
stack<int> cache;
stack<int> pri;
};
/**
* Your StockSpanner object will be instantiated and called as such:
* StockSpanner* obj = new StockSpanner();
* int param_1 = obj->next(price);
*/
作者:dai-52
链接:https://leetcode-cn.com/problems/online-stock-span/solution/gu-piao-jia-ge-kua-du-czhu-shi-fu-can-kao-ti-jie-b/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
肯定是使用bfs,但是遇到一个问题,就是set和map一样,不能保存vector和pair类型,所以怎么储存是一个问题
1.错误的做法
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
//最短路径bfs
int num = 0;
int len1 = grid.size();
int len2 = grid[0].size();
queue<vector<int>> qt;
unordered_set<int> st;
qt.push({0,0});st.insert(make_pair(0,0));
while(!qt.empty())
{
int len = qt.size();
for(int i = 0;i<len;i++)
{
auto temp = qt.front();qt.pop();
if(st.find(temp)==st.end())
{
int x = temp[0],y = temp[1];
if(x==len1-1 && y=len2-1)return num;
if(x-1>=0&&grid[x-1][y]==0)qt.push({x-1,y});
if(y-1>=0&&grid[x][y-1]==0)qt.push({x,y-1});
if(x+1<len1&&grid[x+1][y]==0)qt.push({x+1,y});
if(y+1<len2&&grid[x][y+1]==0)qt.push({x,y+1});
st.insert({x,y});
}
}
num++;
}
return -1;
}
};
2.利用矩阵进行储存,访问过的就置为2之后不再访问
class Solution {
public:
vector<vector<int>>dir={{0,1},{0,-1},{1,0},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
if(grid[0][0]==1)return -1;
int n=grid.size();
queue<pair<int,int>>q;
q.push(make_pair(0,0));
int length=1;
grid[0][0]=2; //将访问过的点标记为2
while(!q.empty()){
int l=q.size(); //遍历当前队列所有的元素
for(int i=0;i<l;i++){
int x=q.front().first;
int y=q.front().second;
q.pop();
if(x==n-1&&y==n-1)return length;
for(int j=0;j<8;j++){
int x1=x+dir[j][0];
int y1=y+dir[j][1];
if(x1<0||y1<0||x1>=n||y1>=n||grid[x1][y1])continue; //越界或者不满足访问条件跳过
grid[x1][y1]=2;
q.push(make_pair(x1,y1));
}
}
length++;
}
return -1;
}
};
作者:mei-you-ni-de-liu-yue-tian
链接:https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/solution/cbiao-zhun-de-bfs-by-mei-you-ni-de-liu-yue-tian/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.利用新的节点,这种方法是最泛化实用性最强的要多学习
struct Node{
int x;
int y;
};
int dpx[8] = {0,0,-1,-1,-1,1,1,1};
int dpy[8] = {-1,1,-1,0,1,-1,0,1};
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
if(grid[0][0] == 1){
return -1;
}
int N = grid.size();
queue<Node> q;
vector<vector<int>> visit = grid;
Node node = {0,0};
q.push(node);
int minPath = 0;
while(!q.empty()){
int len = q.size();
minPath++;
for(int i = 0;i < len;i++){
Node temp = q.front();
q.pop();
if(visit[temp.x][temp.y]){
continue;
}
if(temp.x == N - 1 && temp.y == N-1){
return minPath;
}
visit[temp.x][temp.y] = 1;
for(int i = 0;i<8;i++){
int tempx = temp.x + dpx[i];
int tempy = temp.y + dpy[i];
if(tempx < 0 || tempx >= N || tempy < 0 || tempy >= N){
continue;
}
Node nodeTemp = {tempx,tempy};
q.push(nodeTemp);
}
}
}
return -1;
}
};
作者:jasonnk
链接:https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/solution/er-jin-zhi-ju-zhen-zhong-de-zui-duan-lu-jing-by-ja/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
事先建好容器,很好的方法
class Solution {
public:
bool carPooling(vector<vector<int>>& trips, int capacity) {
vector<int> vt(1000,0);
for(auto trip:trips)
{
for(int i = trip[1] ;i< trip[2]; i++)
{
vt[i] += trip[0];
}
}
for(auto i:vt)
{
if(i>capacity)return false;
}
return true;
}
};
用dp[i][j]表示text1的i号位与text2的j号位之前的LCS长度
根据text1[i]与text2[j]是否相同分为以下两种情况:
1.若text1[i] == text2[j],则dp[i][j] = dp[i-1][j-1] + 1;
2.若text1[i] != text2[j],则dp[i][j] = max(dp[i-1][j], dp[i][j-1])。
作者:zed-65536
链接:https://leetcode-cn.com/problems/longest-common-subsequence/solution/zui-chang-gong-gong-zi-xu-lie-by-zed-65536/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int len1 = text1.size();
int len2 = text2.size();
vector<vector<int>> v(len1+1, vector<int>(len2+1,0));
for(int i = 1 ; i <len1+1 ;i++)
{
for(int j = 1 ; j< len2+1 ; j++)
{
if(text1[i-1]==text2[j-1]) v[i][j] = v[i-1][j-1]+1;
else v[i][j] = max(v[i-1][j], v[i][j-1]);
}
}
return v[len1][len2];
}
};