22下第十一周周赛总结

本文详细介绍了三种算法问题的解决方案:1) 计算具有特定最小公倍数的子数组数量;2) 排序二叉树所需的最少操作次数;3) 不重叠回文子字符串的最大数目。通过字符串哈希和数学方法,实现了高效的算法求解,并提供了清晰的代码实现。
摘要由CSDN通过智能技术生成

力扣单周赛

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)=xy

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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值