力扣单周赛
1.最小公倍数为k的子数组数目
思路
:
g
c
d
(
x
,
y
)
∗
l
c
m
(
x
,
y
)
=
x
∗
y
gcd(x,y)*lcm(x,y)=x*y
gcd(x,y)∗lcm(x,y)=x∗y
class Solution {
public:
int subarrayLCM(vector<int>& nums, int k) {
int n=nums.size();
int ans=0;
for(int i=0;i<n;i++){
int g=nums[i];
for(int j=i;j<n;j++){
g=g*nums[j]/gcd(g,nums[j]); //用新来的nums[j]更新最小公倍数
if(g==k) ans++;
else if(g>k) break;
}
}
return ans;
}
};
2.逐层排序二叉树所需的最少操作次数
思路
:如果当前数字不在它该在的位置上,把让它到他该在的位置上去 如果换过来的数也不该在这个位置就继续换 直到该在这个位置的数归位为止。
代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int work(vector<int> &a){
int n=a.size();
int res=0;
vector<int> b=a;
sort(b.begin(),b.end()); //排序后的位置
unordered_map<int,int> mp;
for(int i=0;i<n;i++) mp[b[i]]=i; //mp存的是排序后的值到下标的映射
for(int i=0;i<n;i++){
while(a[i]!=b[i]){ //如果当前数字不在它该在的位置上
swap(a[i],a[mp[a[i]]]); //把让它到他该在的位置上去 如果换过来的数也不该在这个位置就继续换 直到该在这个位置的数归位为止
res++; //每次交换答案加1
}
}
return res;
}
/*
为什么可以这样贪心?
若两个数对调就能到排序后的位置 那么一次操作可以让两个数归位
若不能那么一次操作只能让一个数归位
因为每次遇到一个数都把它放到它该去的位置上 此后不再动它
例如:7 6 4 5 --发现7不该在这个位置让7归位--> 5 6 4 7 --发现5也不该在这个位置让5归位--> 6 5 4 7 --发现6也不该在这个位置让6归位--> 4 5 6 7
而对于 7 6 5 4 --> 4 6 5 7 --> 4 5 6 7
发现这样操作 每次出现一次交换可以让两个数归位的情况时 我们只用交换一次
若不是上面那种情况 我们每次都可以让一个数归位并且不再动它 所以这样操作得到的操作次数是最小的
*/
int minimumOperations(TreeNode* root) {
int res=0;
queue<TreeNode*> q;
q.push(root);
while(q.size()){
int size=q.size();
vector<int> tmp;
while(size--){
auto it=q.front();
q.pop();
tmp.push_back(it->val);
if(it->left) q.push(it->left);
if(it->right) q.push(it->right);
}
res+=work(tmp);
}
return res;
}
};
3.不重叠回文子字符串的最大数目
思路
:字符串哈希+最长上升子序列 或 字符串哈希+最大不相交区间的数量
代码
字符串哈希+最长上升子序列
typedef unsigned long long ULL;
const int N = 2010, B = 131;
ULL s1[N], s2[N], p[N];
int f[N];
class Solution {
public:
ULL get1(int l, int r)
{
return s1[r] - s1[l - 1] * p[r - l + 1];
}
ULL get2(int l, int r)
{
return s2[l] - s2[r + 1] * p[r - l + 1];
}
int maxPalindromes(string s, int k) {
s = 'a' + s; //让字符串下标从1开始
int n = s.size();
p[0] = 1;
for(int i = 1, j = n - 1; i < n; i ++, j --)
{
s1[i] = s1[i - 1] * B + s[i]; //1~n-1
s2[j] = s2[j + 1] * B + s[j]; //n-1~1
p[i] = p[i - 1] * B;
}
for(int i = 1; i < n; i ++)
{
f[i] = f[i - 1];
for(int j = 1; i - j + 1 >= k; j ++)
{
int mid = i + j >> 1;
int l = mid + (i - j + 1 & 1?-1:0), r = mid + 1;
//若长度为奇数 [j,mid-1] [mid+1,i]
//若长度为偶数 [j,mid] [mid+1,i]
if(get1(j, l) == get2(r, i)) //f[i]表示前i个字符中不重叠的回文串的最大个数
f[i] = max(f[j - 1] + 1, f[i]);
}
}
return f[n - 1];
}
};
字符串哈希+最大不相交区间的数量
class Solution {
public:
typedef pair<int,int> PII;
typedef unsigned long long ULL;
const int N=13331;
int maxPalindromes(string s, int k) {
int res=0;
int n=s.size();
vector<ULL> p(n+1),a1(n+1),a2(n+1);
p[0]=1;
for(int i=0;i<n;i++){ //1~n 从10转移
a1[i+1]=a1[i]*N+s[i];
}
for(int i=n-1;i>=0;i--){ //0~n-1 从n转移
a2[i]=a2[i+1]*N+s[i];
}
for(int i=1;i<=n;i++){
p[i]=p[i-1]*N;
}
vector<PII> arr;
for(int len=k;len<=n;len++){ //区间长度
for(int i=0;i+len-1<n;i++){ //左端点
int j=i+len-1;
ULL t1,t2;
if(len&1){ //长度为奇数 回文有中轴
int mid=(i+j)/2; //找到中心字母
t1=a1[mid]-a1[i]*p[len/2]; //a1数组映射的是字符串[1,n]--->[0,n-1] a[mid]是[0,mid-1]的哈希值 a[i]是[0,i-1]的哈希值 相减得到的是[i,mid-1]的哈希值
t2=a2[mid+1]-a2[j+1]*p[len/2]; //a2数组映射的是字符串[0,n-1]--->[0,n-1] a[mid+1]是[mid+1,n-1]的哈希值 a[j+1,n-1]的哈希值 相减得到的是[mid+1,j]的哈希值
//有中轴的时候比较中轴两边
}
else{ //无中轴 左半边与右半边比较
int mid=(i+j)/2;
t1=a1[mid+1]-a1[i]*p[len/2]; //a[i,mid]
t2=a2[mid+1]-a2[j+1]*p[len/2]; //a[mid+1,j]
}
if(t1==t2) arr.emplace_back(i,j); //把所有满足长度约束的回文区间放入数组
}
}
sort(arr.begin(),arr.end(),[](const PII& x,const PII&y){
return x.second<y.second;
});
int r=-2e9;
for(int i=0;i<arr.size();i++){
if(r<arr[i].first){
res++;
r=arr[i].second;
}
}
return res;
}
};