目录
1461. 检查一个字符串是否包含所有长度为 K 的二进制子串
1414. 和为 K 的最少斐波那契数字数目
给你数字 k ,请你返回和为 k 的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。
斐波那契数字定义为:
F1 = 1
F2 = 1
Fn = Fn-1 + Fn-2 , 其中 n > 2 。
数据保证对于给定的 k ,一定能找到可行解。
示例 1:
输入:k = 7
输出:2
解释:斐波那契数字为:1,1,2,3,5,8,13,……
对于 k = 7 ,我们可以得到 2 + 5 = 7 。
示例 2:
输入:k = 10
输出:2
解释:对于 k = 10 ,我们可以得到 2 + 8 = 10 。
示例 3:
输入:k = 19
输出:3
解释:对于 k = 19 ,我们可以得到 1 + 5 + 13 = 19 。
提示:
1 <= k <= 10^9
int findMinFibonacciNumbers(int k){
int f[45];
f[0] = 0, f[1] = 1;
for (int i = 2; i <= 44; i++)f[i] = f[i - 1] + f[i - 2];
int ans=0;
for(int i=44;i>0;i--)if(k>=f[i])
{
k-=f[i],ans++;
}
return ans;
}
1419. 数青蛙
给你一个字符串 croakOfFrogs,它表示不同青蛙发出的蛙鸣声(字符串 "croak" )的组合。由于同一时间可以有多只青蛙呱呱作响,所以 croakOfFrogs 中会混合多个 “croak” 。请你返回模拟字符串中所有蛙鸣所需不同青蛙的最少数目。
注意:要想发出蛙鸣 "croak",青蛙必须 依序 输出 ‘c’, ’r’, ’o’, ’a’, ’k’ 这 5 个字母。如果没有输出全部五个字母,那么它就不会发出声音。
如果字符串 croakOfFrogs 不是由若干有效的 "croak" 字符混合而成,请返回 -1 。
示例 1:
输入:croakOfFrogs = "croakcroak"
输出:1
解释:一只青蛙 “呱呱” 两次
示例 2:
输入:croakOfFrogs = "crcoakroak"
输出:2
解释:最少需要两只青蛙,“呱呱” 声用黑体标注
第一只青蛙 "crcoakroak"
第二只青蛙 "crcoakroak"
示例 3:
输入:croakOfFrogs = "croakcrook"
输出:-1
解释:给出的字符串不是 "croak" 的有效组合。
示例 4:
输入:croakOfFrogs = "croakcroa"
输出:-1
提示:
1 <= croakOfFrogs.length <= 10^5
字符串中的字符只有 'c', 'r', 'o', 'a' 或者 'k'
class Solution {
public:
int minNumberOfFrogs(string croakOfFrogs) {
map<char,int>m;
int ans=0;
for(int i=0;i<croakOfFrogs.length();i++)
{
m[croakOfFrogs[i]]++;
if(m['c']<m['r'] || m['r']<m['o'] || m['o']<m['a'] || m['a']<m['k'])return -1;
ans=max(ans,m['c']-m['k']);
}
if(m['c']>m['r'] || m['r']>m['o'] || m['o']>m['a'] || m['a']>m['k'])return -1;
return ans;
}
};
1420. 生成数组
DP javascript:void(0)
1424. 对角线遍历 II
给你一个列表 nums ,里面每一个元素都是一个整数列表。请你依照下面各图的规则,按顺序返回 nums 中对角线上的整数。
示例 1:
输入:nums = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,4,2,7,5,3,8,6,9]
示例 2:
输入:nums = [[1,2,3,4,5],[6,7],[8],[9,10,11],[12,13,14,15,16]]
输出:[1,6,2,8,7,3,9,4,12,10,5,13,11,14,15,16]
示例 3:
输入:nums = [[1,2,3],[4],[5,6,7],[8],[9,10,11]]
输出:[1,4,2,5,3,8,6,9,7,10,11]
示例 4:
输入:nums = [[1,2,3,4,5,6]]
输出:[1,2,3,4,5,6]
提示:
1 <= nums.length <= 10^5
1 <= nums[i].length <= 10^5
1 <= nums[i][j] <= 10^9
nums 中最多有 10^5 个数字。
超时代码:
void f(vector<vector<int>>&ans, vector<int>&v)
{
vector<int>tmp;
ans.insert(ans.begin(), tmp);
for (int i = 0; i < v.size(); i++)
{
if (i >= ans.size())ans.push_back(tmp);
ans[i].push_back(v[i]);
}
}
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& nums) {
vector<vector<int>>ans;
for (int i = nums.size() - 1; i >= 0; i--)f(ans, nums[i]);
vector<int>res;
for (int i = 0; i < ans.size(); i++)
{
for (int j = 0; j < ans[i].size(); j++)res.push_back(ans[i][j]);
}
return res;
}
};
其中最致命的问题是,有大量的在vector前面插入元素的操作,
看了同事的js代码之后,我照着她的思路重写了代码:
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& nums) {
map<int,vector<int>>ans;
vector<int>emp;
int k=-1;
for(int i=0;i<nums.size();i++)for(int j=0;j<nums[i].size();j++)
{
if(k<i+j)ans[++k]=emp;
ans[i+j].push_back(nums[i][j]);
}
vector<int>res;
for (int i = 0; i < ans.size(); i++)
{
for (int j = ans[i].size()-1;j>=0;j--)res.push_back(ans[i][j]);
}
return res;
}
};
1441. 用栈操作构建数组
给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3..., n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target :
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。
请返回构建目标数组所用的操作序列。
题目数据保证答案是唯一的。
示例 1:
输入:target = [1,3], n = 3
输出:["Push","Push","Pop","Push"]
解释:
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]
示例 2:
输入:target = [1,2,3], n = 3
输出:["Push","Push","Push"]
示例 3:
输入:target = [1,2], n = 4
输出:["Push","Push"]
解释:只需要读取前 2 个数字就可以停止。
示例 4:
输入:target = [2,3,4], n = 4
输出:["Push","Pop","Push","Push","Push"]
提示:
1 <= target.length <= 100
1 <= target[i] <= 100
1 <= n <= 100
target 是严格递增的
class Solution {
public:
vector<string> buildArray(vector<int>& target, int n) {
vector<string>ans;
int key=0;
for(auto it=target.begin();it!=target.end();it++)
{
int k=*it;
key++;
while(key<k)
{
key++;
ans.insert(ans.end(),"Push");
ans.insert(ans.end(),"Pop");
}
ans.insert(ans.end(),"Push");
}
return ans;
}
};
1442. 形成两个异或相等数组的三元组数目
给你一个整数数组 arr 。
现需要从数组中取三个下标 i、j 和 k ,其中 (0 <= i < j <= k < arr.length) 。
a 和 b 定义如下:
a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]
b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]
注意:^ 表示 按位异或 操作。
请返回能够令 a == b 成立的三元组 (i, j , k) 的数目。
示例 1:
输入:arr = [2,3,1,6,7]
输出:4
解释:满足题意的三元组分别是 (0,1,2), (0,2,2), (2,3,4) 以及 (2,4,4)
示例 2:
输入:arr = [1,1,1,1,1]
输出:10
示例 3:
输入:arr = [2,3]
输出:0
示例 4:
输入:arr = [1,3,5,7,9]
输出:3
示例 5:
输入:arr = [7,11,12,9,5,2,7,17,22]
输出:8
提示:
1 <= arr.length <= 300
1 <= arr[i] <= 10^8
class Solution {
public:
int countTriplets(vector<int>& arr) {
int len=arr.size();
int ans=0;
for(int i=1;i<len;i++)
{
arr[i]^=arr[i-1];
if(arr[i]==0)ans+=i;
}
for(int i=0;i<len;i++)for(int k=i+1;k<len;k++)
if(arr[i]==arr[k])ans+=k-i-1;
return ans;
}
};
1443. 收集树上所有苹果的最少时间
给你一棵有 n 个节点的无向树,节点编号为 0 到 n-1 ,它们中有一些节点有苹果。通过树上的一条边,需要花费 1 秒钟。你从 节点 0 出发,请你返回最少需要多少秒,可以收集到所有苹果,并回到节点 0 。
无向树的边由 edges 给出,其中 edges[i] = [fromi, toi] ,表示有一条边连接 from 和 toi 。除此以外,还有一个布尔数组 hasApple ,其中 hasApple[i] = true 代表节点 i 有一个苹果,否则,节点 i 没有苹果。
示例 1:
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,true,true,false]
输出:8
解释:上图展示了给定的树,其中红色节点表示有苹果。一个能收集到所有苹果的最优方案由绿色箭头表示。
示例 2:
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,false,true,false]
输出:6
解释:上图展示了给定的树,其中红色节点表示有苹果。一个能收集到所有苹果的最优方案由绿色箭头表示。
示例 3:
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,false,false,false,false,false]
输出:0
提示:
1 <= n <= 10^5
edges.length == n-1
edges[i].length == 2
0 <= fromi, toi <= n-1
fromi < toi
hasApple.length == n
class Solution {
public:
int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
int *fa=new int[n];
int *ans=new int[n];
for(int i=0;i<n;i++)ans[i]=0;
ans[0]=1;
for(auto it=edges.begin();it!=edges.end();it++)fa[(*it)[1]]=(*it)[0];
fa[0]=0;
for(int i=0;i<n;i++)if(hasApple[i])
{
int j=i;
while(ans[j]==0)
{
ans[j]=1,j=fa[j];
}
}
int res=-1;
for(int i=0;i<n;i++)if(ans[i])res++;
return res*2;
}
};
1450. 在既定时间做作业的学生人数
给你两个整数数组 startTime(开始时间)和 endTime(结束时间),并指定一个整数 queryTime 作为查询时间。
已知,第 i 名学生在 startTime[i] 时开始写作业并于 endTime[i] 时完成作业。
请返回在查询时间 queryTime 时正在做作业的学生人数。形式上,返回能够使 queryTime 处于区间 [startTime[i], endTime[i]](含)的学生人数。
示例 1:
输入:startTime = [1,2,3], endTime = [3,2,7], queryTime = 4
输出:1
解释:一共有 3 名学生。
第一名学生在时间 1 开始写作业,并于时间 3 完成作业,在时间 4 没有处于做作业的状态。
第二名学生在时间 2 开始写作业,并于时间 2 完成作业,在时间 4 没有处于做作业的状态。
第二名学生在时间 3 开始写作业,预计于时间 7 完成作业,这是是唯一一名在时间 4 时正在做作业的学生。
示例 2:
输入:startTime = [4], endTime = [4], queryTime = 4
输出:1
解释:在查询时间只有一名学生在做作业。
示例 3:
输入:startTime = [4], endTime = [4], queryTime = 5
输出:0
示例 4:
输入:startTime = [1,1,1,1], endTime = [1,3,2,4], queryTime = 7
输出:0
示例 5:
输入:startTime = [9,8,7,6,5,4,3,2,1], endTime = [10,10,10,10,10,10,10,10,10], queryTime = 5
输出:5
提示:
startTime.length == endTime.length
1 <= startTime.length <= 100
1 <= startTime[i] <= endTime[i] <= 1000
1 <= queryTime <= 1000
class Solution {
public:
int busyStudent(vector<int>& startTime, vector<int>& endTime, int queryTime) {
int ans=0;
for(int i=0;i<startTime.size();i++)
if(startTime[i]<=queryTime && queryTime<=endTime[i])ans++;
return ans;
}
};
1451. 重新排列句子中的单词
「句子」是一个用空格分隔单词的字符串。给你一个满足下述格式的句子 text :
句子的首字母大写
text 中的每个单词都用单个空格分隔。
请你重新排列 text 中的单词,使所有单词按其长度的升序排列。如果两个单词的长度相同,则保留其在原句子中的相对顺序。
请同样按上述格式返回新的句子。
示例 1:
输入:text = "Leetcode is cool"
输出:"Is cool leetcode"
解释:句子中共有 3 个单词,长度为 8 的 "Leetcode" ,长度为 2 的 "is" 以及长度为 4 的 "cool" 。
输出需要按单词的长度升序排列,新句子中的第一个单词首字母需要大写。
示例 2:
输入:text = "Keep calm and code on"
输出:"On and keep calm code"
解释:输出的排序情况如下:
"On" 2 个字母。
"and" 3 个字母。
"keep" 4 个字母,因为存在长度相同的其他单词,所以它们之间需要保留在原句子中的相对顺序。
"calm" 4 个字母。
"code" 4 个字母。
示例 3:
输入:text = "To be or not to be"
输出:"To be or to be not"
提示:
text 以大写字母开头,然后包含若干小写字母以及单词间的单个空格。
1 <= text.length <= 10^5
比赛提交的代码:
struct nodes
{
string s;
int k;
};
bool cmp(nodes x, nodes y)
{
if(x.s.length()==y.s.length())return x.k<y.k;
return x.s.length()<y.s.length();
}
class Solution {
public:
string arrangeWords(string text) {
text[0]+='a'-'A';
vector<nodes>v;
v.clear();
int low=0,key=0;
for(int i=0;i<=text.length();i++)
{
if(i==text.length() || text[i]==' ')
{
nodes nod;
nod.s=text.substr(low,i-low);
nod.k=key++;
v.insert(v.end(),nod);
low=i+1;
}
}
sort(v.begin(),v.end(),cmp);
string ans=v[0].s;
for(int i=1;i<v.size();i++)
{
ans+=" ";
ans+=v[i].s;
}
ans[0]+='A'-'a';
return ans;
}
};
几个月后,利用自己的模板重写的代码:
//拓展数据域,加上id
template<typename T>
vector<pair<T,int>>expand(vector<T>v)
{
vector<pair<T,int>>ans;
ans.resize(v.size());
for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
return ans;
}
//提取pair数组的first
template<typename T1,typename T2>
vector<T1> fdraw(vector<pair<T1,T2>>v)
{
vector<T1>ans(v.size());
for(int i=0;i<v.size();i++)ans[i]=v[i].first;
return ans;
}
//给vector拓展,加上id并排序
template<typename T>
bool cmp(pair<T, int> x, pair<T, int> y)
{
if(x.first.length()==y.first.length())return x.second<y.second;
return x.first.length()<y.first.length();
}
template<typename T>
vector<pair<T, int>> sortWithId(vector<T>v)
{
vector<pair<T, int>>ans = expand(v);
sort(ans.begin(), ans.end(),cmp<T>);
return ans;
}
vector<string> stringSplit(string text)
{
vector<string>v;
v.clear();
int low=0,key=0;
for(int i=0;i<=text.length();i++)
{
if(i==text.length() || text[i]==' ')//分隔符
{
v.insert(v.end(),text.substr(low,i-low));
low=i+1;
}
}
return v;
}
class Solution {
public:
string arrangeWords(string text) {
text[0]+='a'-'A';
vector<string>v=fdraw(sortWithId(stringSplit(text)));
string ans=v[0];
for(int i=1;i<v.size();i++)ans+=" "+v[i];
ans[0]+='A'-'a';
return ans;
}
};
1452. 收藏清单
给你一个数组 favoriteCompanies ,其中 favoriteCompanies[i] 是第 i 名用户收藏的公司清单(下标从 0 开始)。
请找出不是其他任何人收藏的公司清单的子集的收藏清单,并返回该清单下标。下标需要按升序排列。
示例 1:
输入:favoriteCompanies = [["leetcode","google","facebook"],["google","microsoft"],["google","facebook"],["google"],["amazon"]]
输出:[0,1,4]
解释:
favoriteCompanies[2]=["google","facebook"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 的子集。
favoriteCompanies[3]=["google"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 和 favoriteCompanies[1]=["google","microsoft"] 的子集。
其余的收藏清单均不是其他任何人收藏的公司清单的子集,因此,答案为 [0,1,4] 。
示例 2:
输入:favoriteCompanies = [["leetcode","google","facebook"],["leetcode","amazon"],["facebook","google"]]
输出:[0,1]
解释:favoriteCompanies[2]=["facebook","google"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 的子集,因此,答案为 [0,1] 。
示例 3:
输入:favoriteCompanies = [["leetcode"],["google"],["facebook"],["amazon"]]
输出:[0,1,2,3]
提示:
1 <= favoriteCompanies.length <= 100
1 <= favoriteCompanies[i].length <= 500
1 <= favoriteCompanies[i][j].length <= 20
favoriteCompanies[i] 中的所有字符串 各不相同 。
用户收藏的公司清单也 各不相同 ,也就是说,即便我们按字母顺序排序每个清单, favoriteCompanies[i] != favoriteCompanies[j] 仍然成立。
所有字符串仅包含小写英文字母。
代码一:
map<string,int>m[105];
class Solution {
public:
vector<int> peopleIndexes(vector<vector<string>>& favoriteCompanies) {
vector<int>ans;
for(int i=0;i<favoriteCompanies.size();i++)
{
m[i].clear();
for(int j=0;j<favoriteCompanies[i].size();j++)m[i][favoriteCompanies[i][j]]=1;
}
for(int i=0;i<favoriteCompanies.size();i++)
{
bool flag=true;
for(int j=0;flag && (j<favoriteCompanies.size());j++)
{
if(j==i)continue;
if(favoriteCompanies[i].size()>favoriteCompanies[j].size())continue;
bool flag2=true;
for(int ii=0;flag2 && (ii<favoriteCompanies[i].size());ii++)if(!m[j][favoriteCompanies[i][ii]])flag2=false;
if(flag2)flag=false;
}
if(flag)ans.insert(ans.end(),i);
}
return ans;
}
};
这是我比赛时候的代码,已经做了一部分优化了(比较列表长短),但是还是超时,超时的用例就是极限用例。
然后我考虑到,把100个列表按照长度排序,每个列表就只需要看比自己长的列表有没有覆盖自己的,把没有被覆盖的列表的ID记下来,这样,每个列表就只需要看比自己长而且没有被任何列表覆盖的列表,我花了20分钟去改,最后没来得及完成。
代码二:
struct node
{
int num;
int id;
};
bool cmp(node x,node y)
{
return x.num>y.num;
}
map<string,int>m[105];
class Solution {
public:
vector<int> peopleIndexes(vector<vector<string>>& favoriteCompanies) {
vector<node>vn;
vector<int>ans;
for(int i=0;i<favoriteCompanies.size();i++)
{
m[i].clear();
for(int j=0;j<favoriteCompanies[i].size();j++)m[i][favoriteCompanies[i][j]]=1;
node nn;
nn.num=favoriteCompanies[i].size(),nn.id=i;
vn.insert(vn.end(),nn);
}
sort(vn.begin(),vn.end(),cmp);
for(int i=0;i<vn.size();i++)
{
bool flag=true;
for(int j=0;flag && (j<i);j++)
{
bool flag2=true;
for(int ii=0;flag2 && (ii<favoriteCompanies[vn[i].num].size());ii++)if(!m[vn[j].num][favoriteCompanies[vn[i].num][ii]])flag2=false;
if(flag2)flag=false;
}
if(flag)ans.insert(ans.end(),vn[i].id);
}
sort(ans.begin(),ans.end());
return ans;
}
};
事后我的2个同事给了我思路,一个是把map改为unordered_map,
只把代码一的第1行改掉,就AC了。
另外一个思路是不用map,把每个列表的字符串排序,然后用双指针的方法判断一个列表是不是覆盖另外一个列表,他这个思路也AC了。
1455. 检查单词是否为句中其他单词的前缀
给你一个字符串 sentence 作为句子并指定检索词为 searchWord ,其中句子由若干用 单个空格 分隔的单词组成。
请你检查检索词 searchWord 是否为句子 sentence 中任意单词的前缀。
如果 searchWord 是某一个单词的前缀,则返回句子 sentence 中该单词所对应的下标(下标从 1 开始)。
如果 searchWord 是多个单词的前缀,则返回匹配的第一个单词的下标(最小下标)。
如果 searchWord 不是任何单词的前缀,则返回 -1 。
字符串 S 的 「前缀」是 S 的任何前导连续子字符串。
示例 1:
输入:sentence = "i love eating burger", searchWord = "burg"
输出:4
解释:"burg" 是 "burger" 的前缀,而 "burger" 是句子中第 4 个单词。
示例 2:
输入:sentence = "this problem is an easy problem", searchWord = "pro"
输出:2
解释:"pro" 是 "problem" 的前缀,而 "problem" 是句子中第 2 个也是第 6 个单词,但是应该返回最小下标 2 。
示例 3:
输入:sentence = "i am tired", searchWord = "you"
输出:-1
解释:"you" 不是句子中任何单词的前缀。
示例 4:
输入:sentence = "i use triple pillow", searchWord = "pill"
输出:4
示例 5:
输入:sentence = "hello from the other side", searchWord = "they"
输出:-1
提示:
1 <= sentence.length <= 100
1 <= searchWord.length <= 10
sentence 由小写英文字母和空格组成。
searchWord 由小写英文字母组成。
前缀就是紧密附着于词根的语素,中间不能插入其它成分,并且它的位置是固定的——-位于词根之前。(引用自 前缀_百度百科 )
class Solution {
public:
int isPrefixOfWord(string sentence, string searchWord) {
int key=1;
for(int i=0,j=0;i<sentence.length();)
{
if(sentence[i]==' ')i++,key++,j=0;
else if(sentence[i]==searchWord[j])
{
i++,j++;
if(j==searchWord.length())return key;
}
else
{
j=0;
while(i<sentence.length() && sentence[i]!=' ')i++;
i++,key++;
}
}
return -1;
}
};
1456. 定长子串中元音的最大数目
给你字符串 s 和整数 k 。
请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a, e, i, o, u)。
示例 1:
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。
示例 2:
输入:s = "aeiou", k = 2
输出:2
解释:任意长度为 2 的子字符串都包含 2 个元音字母。
示例 3:
输入:s = "leetcode", k = 3
输出:2
解释:"lee"、"eet" 和 "ode" 都包含 2 个元音字母。
示例 4:
输入:s = "rhythms", k = 4
输出:0
解释:字符串 s 中不含任何元音字母。
示例 5:
输入:s = "tryhard", k = 4
输出:1
提示:
1 <= s.length <= 10^5
s 由小写英文字母组成
1 <= k <= s.length
int f(char c)
{
if(c=='a'||c=='e'||c=='i'||c=='o'||c=='u')return 1;
return 0;
}
class Solution {
public:
int maxVowels(string s, int k) {
int r=0;
for(int i=0;i<k;i++)r+=f(s[i]);
int ans=r;
for(int i=k;i<s.length();i++)
{
r+=f(s[i])-f(s[i-k]);
ans=max(ans,r);
}
return ans;
}
};
1457. 二叉树中的伪回文路径
给你一棵二叉树,每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值的排列中,存在一个回文序列。
请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。
示例 1:
输入:root = [2,3,1,3,1,null,1]
输出:2
解释:上图为给定的二叉树。总共有 3 条从根到叶子的路径:红色路径 [2,3,3] ,绿色路径 [2,1,1] 和路径 [2,3,1] 。
在这些路径中,只有红色和绿色的路径是伪回文路径,因为红色路径 [2,3,3] 存在回文排列 [3,2,3] ,绿色路径 [2,1,1] 存在回文排列 [1,2,1] 。
示例 2:
输入:root = [2,1,1,1,3,null,null,null,null,null,1]
输出:1
解释:上图为给定二叉树。总共有 3 条从根到叶子的路径:绿色路径 [2,1,1] ,路径 [2,1,3,1] 和路径 [2,1] 。
这些路径中只有绿色路径是伪回文路径,因为 [2,1,1] 存在回文排列 [1,2,1] 。
示例 3:
输入:root = [9]
输出:1
提示:
给定二叉树的节点数目在 1 到 10^5 之间。
节点值在 1 到 9 之间。
class Solution {
public:
int f(TreeNode* root,int n)
{
n^=(1<< root->val);
if(root->right && root->left)return f(root->right,n)+f(root->left,n);
if(root->right)return f(root->right,n);
if(root->left)return f(root->left,n);
return (n&-n)==n;
}
int pseudoPalindromicPaths (TreeNode* root) {
return f(root,0);
}
};
1458. 两个子序列的最大点积
DP javascript:void(0)
1461. 检查一个字符串是否包含所有长度为 K 的二进制子串
给你一个二进制字符串 s 和一个整数 k 。
如果所有长度为 k 的二进制字符串都是 s 的子串,请返回 True ,否则请返回 False 。
示例 1:
输入:s = "00110110", k = 2
输出:true
解释:长度为 2 的二进制串包括 "00","01","10" 和 "11"。它们分别是 s 中下标为 0,1,3,2 开始的长度为 2 的子串。
示例 2:
输入:s = "00110", k = 2
输出:true
示例 3:
输入:s = "0110", k = 1
输出:true
解释:长度为 1 的二进制串包括 "0" 和 "1",显然它们都是 s 的子串。
示例 4:
输入:s = "0110", k = 2
输出:false
解释:长度为 2 的二进制串 "00" 没有出现在 s 中。
示例 5:
输入:s = "0000000001011100", k = 4
输出:false
提示:
1 <= s.length <= 5 * 10^5
s 中只含 0 和 1 。
1 <= k <= 20
class Solution {
public:
bool hasAllCodes(string s, int k) {
unordered_set<string>se;
for(int i=0;i+k<=s.length();i++)
{
se.insert(s.substr(i,k));
if(se.size()+s.length()-i-k<(1<<k))return false;
if(se.size()==(1<<k))return true;
}
return false;
}
};
1464. 数组中两元素的最大乘积
给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。
请你计算并返回该式的最大值。
示例 1:
输入:nums = [3,4,5,2]
输出:12
解释:如果选择下标 i=1 和 j=2(下标从 0 开始),则可以获得最大值,(nums[1]-1)*(nums[2]-1) = (4-1)*(5-1) = 3*4 = 12 。
示例 2:
输入:nums = [1,5,4,5]
输出:16
解释:选择下标 i=1 和 j=3(下标从 0 开始),则可以获得最大值 (5-1)*(5-1) = 16 。
示例 3:
输入:nums = [3,7]
输出:12
提示:
2 <= nums.length <= 500
1 <= nums[i] <= 10^3
class Solution {
public:
int maxProduct(vector<int>& nums) {
int a=0,b=0,c;
for(int i=0;i<nums.size();i++)
{
c=nums[i]-1;
if(b<c)b^=c^=b^=c;
if(a<c)a^=c^=a^=c;
}
return a*b;
}
};
1465. 切割后面积最大的蛋糕
矩形蛋糕的高度为 h 且宽度为 w,给你两个整数数组 horizontalCuts 和 verticalCuts,其中 horizontalCuts[i] 是从矩形蛋糕顶部到第 i 个水平切口的距离,类似地, verticalCuts[j] 是从矩形蛋糕的左侧到第 j 个竖直切口的距离。
请你按数组 horizontalCuts 和 verticalCuts 中提供的水平和竖直位置切割后,请你找出 面积最大 的那份蛋糕,并返回其 面积 。由于答案可能是一个很大的数字,因此需要将结果对 10^9 + 7 取余后返回。
示例 1:
输入:h = 5, w = 4, horizontalCuts = [1,2,4], verticalCuts = [1,3]
输出:4
解释:上图所示的矩阵蛋糕中,红色线表示水平和竖直方向上的切口。切割蛋糕后,绿色的那份蛋糕面积最大。
示例 2:
输入:h = 5, w = 4, horizontalCuts = [3,1], verticalCuts = [1]
输出:6
解释:上图所示的矩阵蛋糕中,红色线表示水平和竖直方向上的切口。切割蛋糕后,绿色和黄色的两份蛋糕面积最大。
示例 3:
输入:h = 5, w = 4, horizontalCuts = [3], verticalCuts = [3]
输出:9
提示:
2 <= h, w <= 10^9
1 <= horizontalCuts.length < min(h, 10^5)
1 <= verticalCuts.length < min(w, 10^5)
1 <= horizontalCuts[i] < h
1 <= verticalCuts[i] < w
题目数据保证 horizontalCuts 中的所有元素各不相同
题目数据保证 verticalCuts 中的所有元素各不相同
class Solution {
public:
int f(int len, vector<int>&v)
{
sort(v.begin(),v.end());
int ans=max(v[0],len-v[v.size()-1]);
for(int i=1;i<v.size();i++)ans=max(ans,v[i]-v[i-1]);
return ans;
}
int maxArea(int h, int w, vector<int>& horizontalCuts, vector<int>& verticalCuts) {
long long a=f(h,horizontalCuts),b=f(w,verticalCuts);
return a*b%1000000007;
}
};
1466. 重新规划路线
DFS、BFS javascript:void(0)
1467. 两个盒子中球的颜色数相同的概率
桌面上有 2n 个颜色不完全相同的球,球上的颜色共有 k 种。给你一个大小为 k 的整数数组 balls ,其中 balls[i] 是颜色为 i 的球的数量。
所有的球都已经 随机打乱顺序 ,前 n 个球放入第一个盒子,后 n 个球放入另一个盒子(请认真阅读示例 2 的解释部分)。
注意:这两个盒子是不同的。例如,两个球颜色分别为 a 和 b,盒子分别为 [] 和 (),那么 [a] (b) 和 [b] (a) 这两种分配方式是不同的(请认真阅读示例 1 的解释部分)。
请计算「两个盒子中球的颜色数相同」的情况的概率。
示例 1:
输入:balls = [1,1]
输出:1.00000
解释:球平均分配的方式只有两种:
- 颜色为 1 的球放入第一个盒子,颜色为 2 的球放入第二个盒子
- 颜色为 2 的球放入第一个盒子,颜色为 1 的球放入第二个盒子
这两种分配,两个盒子中球的颜色数都相同。所以概率为 2/2 = 1 。
示例 2:
输入:balls = [2,1,1]
输出:0.66667
解释:球的列表为 [1, 1, 2, 3]
随机打乱,得到 12 种等概率的不同打乱方案,每种方案概率为 1/12 :
[1,1 / 2,3], [1,1 / 3,2], [1,2 / 1,3], [1,2 / 3,1], [1,3 / 1,2], [1,3 / 2,1], [2,1 / 1,3], [2,1 / 3,1], [2,3 / 1,1], [3,1 / 1,2], [3,1 / 2,1], [3,2 / 1,1]
然后,我们将前两个球放入第一个盒子,后两个球放入第二个盒子。
这 12 种可能的随机打乱方式中的 8 种满足「两个盒子中球的颜色数相同」。
概率 = 8/12 = 0.66667
示例 3:
输入:balls = [1,2,1,2]
输出:0.60000
解释:球的列表为 [1, 2, 2, 3, 4, 4]。要想显示所有 180 种随机打乱方案是很难的,但只检查「两个盒子中球的颜色数相同」的 108 种情况是比较容易的。
概率 = 108 / 180 = 0.6 。
示例 4:
输入:balls = [3,2,1]
输出:0.30000
解释:球的列表为 [1, 1, 1, 2, 2, 3]。要想显示所有 60 种随机打乱方案是很难的,但只检查「两个盒子中球的颜色数相同」的 18 种情况是比较容易的。
概率 = 18 / 60 = 0.3 。
示例 5:
输入:balls = [6,6,6,6,6,6]
输出:0.90327
提示:
1 <= balls.length <= 8
1 <= balls[i] <= 6
sum(balls) 是偶数
答案与真实值误差在 10^-5 以内,则被视为正确答案
思路:
k种颜色的球,从k-1到0依次放球,balls数组存了每种颜色的球的数目。
当放第i个球的时候(i从0到k-1),枚举在左边放球的数量j(j从)
左边还有nl个空位,右边还有nr个空位,那么一共有c[nl+nr][balls[k]]这么多种情况,其中c数组是组合数,
这些情况中,左边放j个,右边放balls[k]-j个的情况数是c[nl][j] * c[nr][balls[k]-j]
所以,这么放的概率是c[nl][j] * c[nr][balls[k]-j] / c[nl+nr][balls[k]]
依次枚举每种颜色的球在左边放的数目,每一种颜色的球的放置都是独立的,不断更新两边的色差,最后色差为0的那些情况的概率加起来,就是最后答案。
代码:
double eps=0.00000001;
class Solution {
public:
double c[51][51];
double ans[25][25][9][20];
vector<int>b;
double getc(int n,int i)
{
if(n<=0||i<=0)return c[n][i]=1;
if(c[n][i]>eps)return c[n][i];
return c[n][i]=getc(n-1,i-1)*n/i;
}
double dp(int nl,int nr,int k,int dif)
{
if(k<0)return (dif==10)?1:0;
if(ans[nl][nr][k][dif]>eps)return ans[nl][nr][k][dif];
int nk=b[k];
double res=0;
for(int i=0;i<=nk;i++)
{
if(nl<i || nr<nk-i)continue;
double p=c[nl][i]*c[nr][nk-i]/c[nl+nr][nk];
int tmpdif=(i?1:0)-((i==nk)?0:1);
res+=p*dp(nl-i,nr-nk+i,k-1,dif-tmpdif);
}
return ans[nl][nr][k][dif]=res;
}
double getProbability(vector<int>& balls) {
b=balls;
for(int i=0;i<50;i++)for(int j=0;j<50;j++)getc(i,j);
int n=0;
for(int i=0;i<b.size();i++)n+=b[i];
return dp(n/2,n/2,b.size()-1,10);
}
};
1470. 重新排列数组
给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,...,xn,y1,y2,...,yn] 的格式排列。
请你将数组按 [x1,y1,x2,y2,...,xn,yn] 格式重新排列,返回重排后的数组。
示例 1:
输入:nums = [2,5,1,3,4,7], n = 3
输出:[2,3,5,4,1,7]
解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]
示例 2:
输入:nums = [1,2,3,4,4,3,2,1], n = 4
输出:[1,4,2,3,3,2,4,1]
示例 3:
输入:nums = [1,1,2,2], n = 2
输出:[1,2,1,2]
提示:
1 <= n <= 500
nums.length == 2n
1 <= nums[i] <= 10^3
class Solution {
public:
vector<int> shuffle(vector<int>& nums, int n) {
vector<int>ans;
for(int i=0;i<n;i++)
{
ans.push_back(nums[i]);
ans.push_back(nums[n+i]);
}
return ans;
}
};
1471. 数组中的 k 个最强值
给你一个整数数组 arr 和一个整数 k 。
设 m 为数组的中位数,只要满足下述两个前提之一,就可以判定 arr[i] 的值比 arr[j] 的值更强:
|arr[i] - m| > |arr[j] - m|
|arr[i] - m| == |arr[j] - m|,且 arr[i] > arr[j]
请返回由数组中最强的 k 个值组成的列表。答案可以以 任意顺序 返回。
中位数 是一个有序整数列表中处于中间位置的值。形式上,如果列表的长度为 n ,那么中位数就是该有序列表(下标从 0 开始)中位于 ((n - 1) / 2) 的元素。
例如 arr = [6, -3, 7, 2, 11],n = 5:数组排序后得到 arr = [-3, 2, 6, 7, 11] ,数组的中间位置为 m = ((5 - 1) / 2) = 2 ,中位数 arr[m] 的值为 6 。
例如 arr = [-7, 22, 17, 3],n = 4:数组排序后得到 arr = [-7, 3, 17, 22] ,数组的中间位置为 m = ((4 - 1) / 2) = 1 ,中位数 arr[m] 的值为 3 。
示例 1:
输入:arr = [1,2,3,4,5], k = 2
输出:[5,1]
解释:中位数为 3,按从强到弱顺序排序后,数组变为 [5,1,4,2,3]。最强的两个元素是 [5, 1]。[1, 5] 也是正确答案。
注意,尽管 |5 - 3| == |1 - 3| ,但是 5 比 1 更强,因为 5 > 1 。
示例 2:
输入:arr = [1,1,3,5,5], k = 2
输出:[5,5]
解释:中位数为 3, 按从强到弱顺序排序后,数组变为 [5,5,1,1,3]。最强的两个元素是 [5, 5]。
示例 3:
输入:arr = [6,7,11,7,6,8], k = 5
输出:[11,8,6,6,7]
解释:中位数为 7, 按从强到弱顺序排序后,数组变为 [11,8,6,6,7,7]。
[11,8,6,6,7] 的任何排列都是正确答案。
示例 4:
输入:arr = [6,-3,7,2,11], k = 3
输出:[-3,11,2]
示例 5:
输入:arr = [-7,22,17,3], k = 2
输出:[22,17]
提示:
1 <= arr.length <= 10^5
-10^5 <= arr[i] <= 10^5
1 <= k <= arr.length
class Solution {
public:
vector<int> getStrongest(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
int mid=arr[(arr.size()-1)/2];
int low=0,high=arr.size()-1;
vector<int>ans;
while(k--)
{
if(abs(arr[low]-mid)>abs(arr[high]-mid) || abs(arr[low]-mid)==abs(arr[high]-mid) && arr[low]>arr[high])ans.push_back(arr[low++]);
else ans.push_back(arr[high--]);
}
return ans;
}
};
1472. 设计浏览器历史记录
你有一个只支持单个标签页的 浏览器 ,最开始你浏览的网页是 homepage ,你可以访问其他的网站 url ,也可以在浏览历史中后退 steps 步或前进 steps 步。
请你实现 BrowserHistory 类:
BrowserHistory(string homepage) ,用 homepage 初始化浏览器类。
void visit(string url) 从当前页跳转访问 url 对应的页面 。执行此操作会把浏览历史前进的记录全部删除。
string back(int steps) 在浏览历史中后退 steps 步。如果你只能在浏览历史中后退至多 x 步且 steps > x ,那么你只后退 x 步。请返回后退 至多 steps 步以后的 url 。
string forward(int steps) 在浏览历史中前进 steps 步。如果你只能在浏览历史中前进至多 x 步且 steps > x ,那么你只前进 x 步。请返回前进 至多 steps步以后的 url 。
示例:
输入:
["BrowserHistory","visit","visit","visit","back","back","forward","visit","forward","back","back"]
[["leetcode.com"],["google.com"],["facebook.com"],["youtube.com"],[1],[1],[1],["linkedin.com"],[2],[2],[7]]
输出:
[null,null,null,null,"facebook.com","google.com","facebook.com",null,"linkedin.com","google.com","leetcode.com"]
解释:
BrowserHistory browserHistory = new BrowserHistory("leetcode.com");
browserHistory.visit("google.com"); // 你原本在浏览 "leetcode.com" 。访问 "google.com"
browserHistory.visit("facebook.com"); // 你原本在浏览 "google.com" 。访问 "facebook.com"
browserHistory.visit("youtube.com"); // 你原本在浏览 "facebook.com" 。访问 "youtube.com"
browserHistory.back(1); // 你原本在浏览 "youtube.com" ,后退到 "facebook.com" 并返回 "facebook.com"
browserHistory.back(1); // 你原本在浏览 "facebook.com" ,后退到 "google.com" 并返回 "google.com"
browserHistory.forward(1); // 你原本在浏览 "google.com" ,前进到 "facebook.com" 并返回 "facebook.com"
browserHistory.visit("linkedin.com"); // 你原本在浏览 "facebook.com" 。 访问 "linkedin.com"
browserHistory.forward(2); // 你原本在浏览 "linkedin.com" ,你无法前进任何步数。
browserHistory.back(2); // 你原本在浏览 "linkedin.com" ,后退两步依次先到 "facebook.com" ,然后到 "google.com" ,并返回 "google.com"
browserHistory.back(7); // 你原本在浏览 "google.com", 你只能后退一步到 "leetcode.com" ,并返回 "leetcode.com"
提示:
1 <= homepage.length <= 20
1 <= url.length <= 20
1 <= steps <= 100
homepage 和 url 都只包含 '.' 或者小写英文字母。
最多调用 5000 次 visit, back 和 forward 函数。
//id处,覆盖或者添加x
template<typename T>
void finsert(vector<T>&v,int id,T x)
{
if(id<0||id>v.size())return;
if(id==v.size())v.push_back(x);
v[id]=x;
}
class BrowserHistory {
public:
vector<string>v;
int k,len;
BrowserHistory(string homepage) {
v.push_back(homepage),k=0,len=1;
}
void visit(string url) {
finsert(v,++k,url);
len=k+1;
}
string back(int steps) {
return v[k-=min(steps,k)];
}
string forward(int steps) {
return v[k+=min(steps,len-k-1)];
}
};
1473. 给房子涂色 III
DP javascript:void(0)
1480. 一维数组的动态和
给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。
请返回 nums 的动态和。
示例 1:
输入:nums = [1,2,3,4]
输出:[1,3,6,10]
解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。
示例 2:
输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。
示例 3:
输入:nums = [3,1,2,10,1]
输出:[3,4,6,16,17]
提示:
1 <= nums.length <= 1000
-10^6 <= nums[i] <= 10^6
class Solution {
public:
vector<int> runningSum(vector<int>& nums) {
for(int i=1;i<nums.size();i++)nums[i]+=nums[i-1];
return nums;
}
};
1481. 不同整数的最少数目
给你一个整数数组 arr 和一个整数 k 。现需要从数组中恰好移除 k 个元素,请找出移除后数组中不同整数的最少数目。
示例 1:
输入:arr = [5,5,4], k = 1
输出:1
解释:移除 1 个 4 ,数组中只剩下 5 一种整数。
示例 2:
输入:arr = [4,3,1,1,3,3,2], k = 3
输出:2
解释:先移除 4、2 ,然后再移除两个 1 中的任意 1 个或者三个 3 中的任意 1 个,最后剩下 1 和 3 两种整数。
提示:
1 <= arr.length <= 10^5
1 <= arr[i] <= 10^9
0 <= k <= arr.length
思路:
先计数,再排序,再贪心,就可以了。
class Solution {
public:
int findLeastNumOfUniqueInts(vector<int>& arr, int k) {
unordered_map<int,int>m;
for(int i=0;i<arr.size();i++)m[arr[i]]++;
vector<int>ans;
for(auto i=m.begin();i!=m.end();i++)ans.push_back(i->second);
sort(ans.begin(),ans.end());
int i;
for(i=0;i<ans.size();i++)
{
if(k>ans[i])k-=ans[i];
else if(k==ans[i])return ans.size()-i-1;
else return ans.size()-i;
}
return 0;
}
};
1482. 制作 m 束花所需的最少天数
二分、三分 javascript:void(0)
1483. 树节点的第 K 个祖先
给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。
请你设计并实现 getKthAncestor(int node, int k) 函数,函数返回节点 node 的第 k 个祖先节点。如果不存在这样的祖先节点,返回 -1 。
树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。
示例:
输入:
["TreeAncestor","getKthAncestor","getKthAncestor","getKthAncestor"]
[[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]]
输出:
[null,1,0,-1]
解释:
TreeAncestor treeAncestor = new TreeAncestor(7, [-1, 0, 0, 1, 1, 2, 2]);
treeAncestor.getKthAncestor(3, 1); // 返回 1 ,它是 3 的父节点
treeAncestor.getKthAncestor(5, 2); // 返回 0 ,它是 5 的祖父节点
treeAncestor.getKthAncestor(6, 3); // 返回 -1 因为不存在满足要求的祖先节点
提示:
1 <= k <= n <= 5*10^4
parent[0] == -1 表示编号为 0 的节点是根节点。
对于所有的 0 < i < n ,0 <= parent[i] < n 总成立
0 <= node < n
至多查询 5*10^4 次
这个题有极限用例,5万个结点,单链树(深度为5万),查询次数5万,每次查询的k也是4-5万。
C++暴力是过不了的,但是js暴力能过。
分析:
如果不使用任何数据结构和算法,那么对于单次查询来说,5万次计算是必不可少的,
而对于多次查询来说,既然暴力不能过,那就只有两种情况:
(1)通过数据结构,采用信息转换的方式,降低单次查询时间
PS:哈希,字典树,状态压缩等,都属于我理解的信息转换,而这题的LCA解法(BFS+DFS+二分)就是属于这一种
(2)通过算法整改解空间,去掉多次查询之间的重复计算
PS:本题的倍增法本质就是动态规划,但是可能动态规划的特征没那么明显
思路一:
用f(node, k)表示答案,在我们求出f(100,50)=x之后,如果再求f(100,51),直接用f(x,1)来求就行了。
可是,如果先求f(100,51),再求(100,50)呢?
如果我们在求f(100,51)的过程中,把f(100,1) ......f(100,51)都存起来,那么这个算法光空间复杂度就n^2了,不行。
继续分析这个大小为n^2的解空间,哪些信息是重复的?
很明显是有的,如果我们存了(100,1) ......f(100,10)=y,也存了f(y,1)......f(y,40),那么f(100,11)......f(51)还有必要存吗?
没有必要,只要跳转一次就行了!
假设每个节点只存f(n,1)......f(n,m),那么空间复杂度n*m,时间复杂度n*m+n*n/m,也就是n^1.5,大概等于1千万。
PS:这个思路的前面一半是我比赛时的实际思路,然后我想到带权并查集,然后放弃了这个思路。
PPS:和我 CSU 1515: Sequence javascript:void(0) 这个题的做法也比较像。
赛后代码:
int ans[50005][201];
class TreeAncestor {
public:
vector<int>p;
int n;
TreeAncestor(int n, vector<int>& parent) {
p=parent,this->n=n;
for(int i=0;i<n;i++)ans[i][0]=i;
for(int d=1;d<=200;d++)for(int i=0;i<n;i++)ans[i][d]=ans[i][d-1]>=0?parent[ans[i][d-1]]:-1;
}
int getKthAncestor(int node, int k) {
while(k>200)
{
node=ans[node][200],k-=200;
if(node==-1)return -1;
}
return ans[node][k];
}
};
思路二:倍增法
参考树状数组,用ans[i][j]表示第i个结点的第2^j个祖先
时间复杂度和空间复杂度都是O(nlogn)
比赛代码:
int ans[50005][18];
class TreeAncestor {
public:
vector<int>p;
int n;
TreeAncestor(int n, vector<int>& parent) {
p=parent,this->n=n;
for(int i=0;i<n;i++)ans[i][1]=parent[i];
for(int d=2;d<18;d++)for(int i=0;i<n;i++)ans[i][d]=ans[i][d-1]>=0?ans[ans[i][d-1]][d-1]:-1;
}
int getKthAncestor(int node, int k) {
for(int i=1;i<18;i++)
{
if(k&(1<<(i-1)))node=ans[node][i];
if(node==-1)return -1;
}
return node;
}
};
思路三:
力扣题解中的BFS+DFS+二分
PS:
LCA最近公共祖先问题,可以用BFS+DFS+RMQ区间极值来做,也可以用倍增法来做。
这个题的倍增法,和LCA的倍增法差不多。
这个题的BFS+DFS+二分解法,和LCA的BFS+DFS+RMQ解法差不多。
1486. 数组异或操作
给你两个整数,n
和 start
。
数组 nums
定义为:nums[i] = start + 2*i
(下标从 0 开始)且 n == nums.length
。
请返回 nums
中所有元素按位异或(XOR)后得到的结果。
示例 1:
输入:n = 5, start = 0
输出:8
解释:数组 nums 为 [0, 2, 4, 6, 8],其中 (0 ^ 2 ^ 4 ^ 6 ^ 8) = 8 。
"^" 为按位异或 XOR 运算符。
示例 2:
输入:n = 4, start = 3
输出:8
解释:数组 nums 为 [3, 5, 7, 9],其中 (3 ^ 5 ^ 7 ^ 9) = 8.
示例 3:
输入:n = 1, start = 7
输出:7
示例 4:
输入:n = 10, start = 5
输出:2
提示:
-
1 <= n <= 1000
-
0 <= start <= 1000
-
n == nums.length
class Solution {
public:
int xorOperation(int n, int start) {
if(n==1)return start;
return start^xorOperation(n-1,start+2);
}
};
1487. 保证文件名唯一
给你一个长度为 n
的字符串数组 names
。你将会在文件系统中创建 n
个文件夹:在第 i
分钟,新建名为 names[i]
的文件夹。
由于两个文件 不能 共享相同的文件名,因此如果新建文件夹使用的文件名已经被占用,系统会以 (k)
的形式为新文件夹的文件名添加后缀,其中 k
是能保证文件名唯一的 最小正整数 。
返回长度为 n
的字符串数组,其中 ans[i]
是创建第 i
个文件夹时系统分配给该文件夹的实际名称。
示例 1:
输入:names = ["pes","fifa","gta","pes(2019)"]
输出:["pes","fifa","gta","pes(2019)"]
解释:文件系统将会这样创建文件名:
"pes" --> 之前未分配,仍为 "pes"
"fifa" --> 之前未分配,仍为 "fifa"
"gta" --> 之前未分配,仍为 "gta"
"pes(2019)" --> 之前未分配,仍为 "pes(2019)"
示例 2:
输入:names = ["gta","gta(1)","gta","avalon"]
输出:["gta","gta(1)","gta(2)","avalon"]
解释:文件系统将会这样创建文件名:
"gta" --> 之前未分配,仍为 "gta"
"gta(1)" --> 之前未分配,仍为 "gta(1)"
"gta" --> 文件名被占用,系统为该名称添加后缀 (k),由于 "gta(1)" 也被占用,所以 k = 2 。实际创建的文件名为 "gta(2)" 。
"avalon" --> 之前未分配,仍为 "avalon"
示例 3:
输入:names = ["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece"]
输出:["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece(4)"]
解释:当创建最后一个文件夹时,最小的正有效 k 为 4 ,文件名变为 "onepiece(4)"。
示例 4:
输入:names = ["wano","wano","wano","wano"]
输出:["wano","wano(1)","wano(2)","wano(3)"]
解释:每次创建文件夹 "wano" 时,只需增加后缀中 k 的值即可。
示例 5:
输入:names = ["kaido","kaido(1)","kaido","kaido(1)"]
输出:["kaido","kaido(1)","kaido(2)","kaido(1)(1)"]
解释:注意,如果含后缀文件名被占用,那么系统也会按规则在名称后添加新的后缀 (k) 。
提示:
-
1 <= names.length <= 5 * 10^4
-
1 <= names[i].length <= 20
-
names[i]
由小写英文字母、数字和/或圆括号组成。
class Solution {
public:
vector<string> getFolderNames(vector<string>& names) {
unordered_map<string,int>m;
m.reserve(50005);
vector<string>ans;
char cn[6];
for(int i=0;i<names.size();i++)
{
string s=names[i],s1;
if(m[s]==0)
{
ans.push_back(s);
m[s]=1;
continue;
}
for(int i=m[s];i<50005;i++)
{
sprintf(cn,"%d",i);
s1=s+"("+cn+")";
if(m[s1]==0)
{
ans.push_back(s1);
m[s]=i+1;
m[s1]=1;
break;
}
}
}
return ans;
}
};
PS:
我WA的用例:
["kaido","kaido(1)","kaido","kaido(1)","kaido(2)"]
["kaido","kaido(1)","kaido(2)","kaido(1)(1)","kaido(2)(1)"]
修改代码:
s和s1都要存入map,改完AC
1488. 避免洪水泛滥
线段树 javascript:void(0)
1496. 判断路径是否相交
给你一个字符串 path,其中 path[i] 的值可以是 'N'、'S'、'E' 或者 'W',分别表示向北、向南、向东、向西移动一个单位。
机器人从二维平面上的原点 (0, 0) 处开始出发,按 path 所指示的路径行走。
如果路径在任何位置上出现相交的情况,也就是走到之前已经走过的位置,请返回 True ;否则,返回 False 。
示例 1:
输入:path = "NES"
输出:false
解释:该路径没有在任何位置相交。
示例 2:
输入:path = "NESWW"
输出:true
解释:该路径经过原点两次。
提示:
1 <= path.length <= 10^4
path 仅由 {'N', 'S', 'E', 'W} 中的字符组成
C++代码:
class Solution {
public:
bool isPathCrossing(string path) {
int ns=0,ew=0;
map<pair<int,int>,int>m;
m[make_pair(ns,ew)]=1;
for(int i=0;path[i];i++)
{
if(path[i]=='N')ns++;
if(path[i]=='S')ns--;
if(path[i]=='E')ew++;
if(path[i]=='W')ew--;
if(m[make_pair(ns,ew)])return true;
m[make_pair(ns,ew)]=1;
}
return false;
}
};
C代码:
typedef struct {
int key;
int value;
UT_hash_handle hh;
} Hash;
Hash *hash = NULL;
// 增加
void add(int key, int value)
{
Hash *s = NULL;
s = (Hash *)malloc(sizeof(Hash));
s->key = key;
s->value = value;
HASH_ADD_INT(hash, key, s);
}
// 查找
int find(int key)
{
Hash *s = NULL;
HASH_FIND_INT(hash, &key, s);
if (s != NULL) {
// 查找到结果
return 1;
} else {
return 0;
}
}
// 删除
void delete (Hash *s)
{
HASH_DEL(hash, s);
free(s);
s = NULL;
}
//清空
void clearAll()
{
Hash *s,*tmp;
HASH_ITER(hh, hash, s, tmp){
delete(s);
}
}
bool isPathCrossing(char * path){
int ns=0,ew=0;
add(0,1);
for(int i=0;path[i];i++)
{
if(path[i]=='N')ns++;
if(path[i]=='S')ns--;
if(path[i]=='E')ew++;
if(path[i]=='W')ew--;
if(find(ns*10000+ew))
{
clearAll();
return true;
}
add(ns*10000+ew,1);
}
clearAll();
return false;
}
1502. 判断能否形成等差数列
给你一个数字数组 arr 。
如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。
如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false 。
示例 1:
输入:arr = [3,5,1]
输出:true
解释:对数组重新排序得到 [1,3,5] 或者 [5,3,1] ,任意相邻两项的差分别为 2 或 -2 ,可以形成等差数列。
示例 2:
输入:arr = [1,2,4]
输出:false
解释:无法通过重新排序得到等差数列。
提示:
2 <= arr.length <= 1000
-10^6 <= arr[i] <= 10^6
class Solution {
public:
bool canMakeArithmeticProgression(vector<int>& arr) {
sort(arr.begin(), arr.end());
int k = arr[1] - arr[0];
for(int i = 2; i < arr.size(); i++)if (arr[i] - arr[i - 1] - k)return false;
return true;
}
};
1503. 所有蚂蚁掉下来前的最后一刻
有一块木板,长度为 n 个 单位 。一些蚂蚁在木板上移动,每只蚂蚁都以 每秒一个单位 的速度移动。其中,一部分蚂蚁向 左 移动,其他蚂蚁向 右 移动。
当两只向 不同 方向移动的蚂蚁在某个点相遇时,它们会同时改变移动方向并继续移动。假设更改方向不会花费任何额外时间。
而当蚂蚁在某一时刻 t 到达木板的一端时,它立即从木板上掉下来。
给你一个整数 n 和两个整数数组 left 以及 right 。两个数组分别标识向左或者向右移动的蚂蚁在 t = 0 时的位置。请你返回最后一只蚂蚁从木板上掉下来的时刻。
示例 1:
输入:n = 4, left = [4,3], right = [0,1]
输出:4
解释:如上图所示:
-下标 0 处的蚂蚁命名为 A 并向右移动。
-下标 1 处的蚂蚁命名为 B 并向右移动。
-下标 3 处的蚂蚁命名为 C 并向左移动。
-下标 4 处的蚂蚁命名为 D 并向左移动。
请注意,蚂蚁在木板上的最后时刻是 t = 4 秒,之后蚂蚁立即从木板上掉下来。(也就是说在 t = 4.0000000001 时,木板上没有蚂蚁)。
示例 2:
输入:n = 7, left = [], right = [0,1,2,3,4,5,6,7]
输出:7
解释:所有蚂蚁都向右移动,下标为 0 的蚂蚁需要 7 秒才能从木板上掉落。
示例 3:
输入:n = 7, left = [0,1,2,3,4,5,6,7], right = []
输出:7
解释:所有蚂蚁都向左移动,下标为 7 的蚂蚁需要 7 秒才能从木板上掉落。
示例 4:
输入:n = 9, left = [5], right = [4]
输出:5
解释:t = 1 秒时,两只蚂蚁将回到初始位置,但移动方向与之前相反。
示例 5:
输入:n = 6, left = [6], right = [0]
输出:6
提示:
1 <= n <= 10^4
0 <= left.length <= n + 1
0 <= left[i] <= n
0 <= right.length <= n + 1
0 <= right[i] <= n
1 <= left.length + right.length <= n + 1
left 和 right 中的所有值都是唯一的,并且每个值 只能出现在二者之一 中。
class Solution {
public:
int getLastMoment(int n, vector<int>& left, vector<int>& right) {
int ans = 0;
for (int i = 0; i < left.size(); i++)ans = max(ans, left[i]);
for (int i = 0; i < right.size(); i++)ans = max(ans, n-right[i]);
return ans;
}
};
1504. 统计全 1 子矩形
DP javascript:void(0)
1512. 好数对的数目
给你一个整数数组 nums
。
如果一组数字 (i,j)
满足 nums[i]
== nums[j]
且 i
< j
,就可以认为这是一组 好数对 。
返回好数对的数目。
示例 1:
输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始
示例 2:
输入:nums = [1,1,1,1]
输出:6
解释:数组中的每组数字都是好数对
示例 3:
输入:nums = [1,2,3]
输出:0
提示:
-
1 <= nums.length <= 100
-
1 <= nums[i] <= 100
class Solution {
public:
int numIdenticalPairs(vector<int>& nums) {
int ans=0;
for(int i=0;i<nums.size();i++)for(int j=i+1;j<nums.size();j++)ans+=(nums[i]==nums[j]);
return ans;
}
};
1513. 仅含 1 的子串数
给你一个二进制字符串 s
(仅由 '0' 和 '1' 组成的字符串)。
返回所有字符都为 1 的子字符串的数目。
由于答案可能很大,请你将它对 10^9 + 7 取模后返回。
示例 1:
输入:s = "0110111"
输出:9
解释:共有 9 个子字符串仅由 '1' 组成
"1" -> 5 次
"11" -> 3 次
"111" -> 1 次
示例 2:
输入:s = "101"
输出:2
解释:子字符串 "1" 在 s 中共出现 2 次
示例 3:
输入:s = "111111"
输出:21
解释:每个子字符串都仅由 '1' 组成
示例 4:
输入:s = "000"
输出:0
提示:
-
s[i] == '0'
或 s[i] == '1'
-
1 <= s.length <= 10^5
class Solution {
public:
int numSub(string s) {
int len=0,ans=0;
for(int i=0;i<s.length();i++)
{
if(s[i]-'0')len++;
else len=0;
ans+=len,ans%=1000000007;
}
return ans;
}
};