每天一道LeetCode ( 已经鸽了


LeetCode真友好,题意直截了当,爽到。


1187. 使数组严格递增(2020.3.25)

题意:

给你两个整数数组 arr1 和 arr2,返回使 arr1 严格递增所需要的最小「操作」数(可能为 0)。
每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,分别为 i 和 j,0 <= i < arr1.length 和 0 <= j < arr2.length,然后进行赋值运算 arr1[i] = arr2[j]。
如果无法让 arr1 严格递增,请返回 -1。

数据范围:
1 <= arr1.length, arr2.length <= 2000
0 <= arr1[i], arr2[i] <= 10^9

ac:

在这里插入图片描述

思路:

因为只需要比大小,所以离散化。
观察到数据范围只有2000,令d(i,j)表示前i个数保持严格递增,且结尾为j的最少操作次数,两层循环dp一下就行了。
代码顺便优化了一下空间。

code:

class Solution {
public:
    int makeArrayIncreasing(vector<int>& arr1, vector<int>& arr2) {
        int n=arr1.size(),m=arr2.size();
        //离散化
        sort(arr2.begin(),arr2.end());
        m=unique(arr2.begin(),arr2.end())-arr2.begin();
        vector<int>temp;
        for(int i=0;i<n;i++)temp.push_back(arr1[i]);
        for(int i=0;i<m;i++)temp.push_back(arr2[i]);
        sort(temp.begin(),temp.end());
        int num=unique(temp.begin(),temp.end())-temp.begin();
        for(int i=0;i<n;i++)arr1[i]=lower_bound(temp.begin(),temp.begin()+num,arr1[i])-temp.begin();
        for(int i=0;i<m;i++)arr2[i]=lower_bound(temp.begin(),temp.begin()+num,arr2[i])-temp.begin();
        //dp
        int d[2][num],pos=0;
        for(int i=0;i<2;i++)for(int j=0;j<num;j++)d[i][j]=1e9;
        for(int i=0;i<m;i++)d[pos][arr2[0]]=1;
        d[pos][arr1[0]]=0;
        for(int i=1;i<n;i++){
            pos^=1;
            for(int j=0;j<num;j++)d[pos][j]=1e9;
            for(int j=0;j<arr1[i];j++){//不换
                d[pos][arr1[i]]=min(d[pos][arr1[i]],d[pos^1][j]);
            }
            int mi=1e9;
            int k=0;
            for(int j=0;j<m;j++){//换
                while(k<num&&k<arr2[j]){
                    mi=min(mi,d[pos^1][k]);
                    k++;
                }
                d[pos][arr2[j]]=min(d[pos][arr2[j]],mi+1);
            }
        }
        //ans
        int ans=1e9;
        for(int i=0;i<num;i++){
            ans=min(ans,d[pos][i]);
        }
        if(ans==1e9)ans=-1;
        return ans;
    }
};

139. 单词拆分(2020.3.26)

题意:

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

ac:

在这里插入图片描述

思路:

令d(i)表示位置i前面是否能加空格,如果存在j<i且d(j)=1且s.substr(j,i)是单词,则d(i)=1
这样就得到了一个O(n2)的算法。
考虑到只需要找满足d(j)=1的j,d(j)=0的可以忽略,因此用vector存d(j)=1的j,优化了第二维。

代码里面还有乱七八糟的小优化

code:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        map<string,bool>mark;
        int maxsize=0;
        for(string i:wordDict){
            mark[i]=1;
            int len1=i.size();
            if(len1>maxsize)maxsize=len1;
        }
        vector<int>d;
        d.push_back(0);
        int len=s.size();
        for(int i=1;i<=len;i++){
            //随便优化一下
            while(!d.empty()&&i-d[0]>maxsize)d.erase(d.begin());
            if(d.empty())return 0;
            //
            for(int v:d){
                if(i-v>maxsize)continue;
                if(mark.count(s.substr(v,i-v))){
                    d.push_back(i);
                    break;
                }
            }
        }
        return *(d.end()-1)==len;
    }
};

76. 最小覆盖子串(2020.3.27)

题意:

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

ac:

在这里插入图片描述

code:

class Solution {
public:
    string minWindow(string s, string t) {
        //处理
        int len=s.size();
        int mark[128]={0},sz=0;
        for(char v:t){
            mark[v]++;
            if(mark[v]==1)sz++;
        }
        //计算
        int milen=len+1;
        int pos=-1;
        int now[128]={0};
        int sum=0;
        int l=0;
        for(int i=0;i<len;i++){
            now[s[i]]++;
            if(now[s[i]]==mark[s[i]])sum++;
            while(sum==sz){
                int nowlen=i-l+1;
                if(nowlen<milen){
                    milen=nowlen;
                    pos=l;
                }
                now[s[l]]--;
                if(now[s[l]]==mark[s[l]]-1)sum--;
                l++;
            }
        }
        if(pos==-1)return "";
        else return s.substr(pos,milen);
    }
};

72. 编辑距离(2020.3.28)

题意:

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
1.插入一个字符
2.删除一个字符
3.替换一个字符

ac:

在这里插入图片描述

code:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n=word1.size(),m=word2.size();
        int d[n+1][m+1];
        for(int i=0;i<=n;i++){
            d[i][0]=i;
        }
        for(int j=0;j<=m;j++){
            d[0][j]=j;
        }
        for(int j=1;j<=m;j++){
            for(int i=1;i<=n;i++){
                if(word2[j-1]==word1[i-1]){//dp数组下标从1开始,所以这里是i-1和j-1
                    d[i][j]=d[i-1][j-1];
                }else{
                    d[i][j]=min(min(d[i-1][j],d[i][j-1]),d[i-1][j-1])+1;
                }
            }
        }
        return d[n][m];
    }
};
/*
d[i][j]表示s串前i个字符变为t串前j个字符的最小操作次数

如果s[i]==t[i],那么d[i][j]=d[i-1][j-1]

如果s[i]!=t[i],那么:
增加:d[i][j-1]+1
删除:d[i-1][j]+1
替换:d[i-1][j-1]+1
对上面三者取min来更新d[i][j]
*/

629. K个逆序对数组(2020.3.29)

题意:

给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。
逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j且 a[i] > a[j],则其为一个逆序对;否则不是。
由于答案可能很大,只需要返回 答案 mod 109 + 7 的值。

ac:

在这里插入图片描述

code:

class Solution {
public:
    int kInversePairs(int n, int k) {
        const int mod=1e9+7;
        int d[n+1][k+1];
        for(int i=0;i<=n;i++){//初始化
            d[i][0]=1;
            for(int j=1;j<=k;j++)d[i][j]=0;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=k;j++){
                //d[i][j]=d[i][j-1]-d[i-1][(j-1)-(i-1)]+d[i-1][j]
                d[i][j]=d[i][j-1]+d[i-1][j];
                if(j-i>=0)d[i][j]-=d[i-1][j-i];
                d[i][j]=(d[i][j]%mod+mod)%mod;
            }
        }
        return d[n][k];
    }
};
/*
d[i][j]表示1到i组成逆序对数量为j的方案数

如果计算出前i-1的答案,考虑将i插入前i-1中:
因为此时i是最大的数,插入位置后面有多少个数,逆序对就增加多少
插入到最后的时候,逆序对增加0,则d[i][j]+=d[i-1][j]
插入到倒数第二个位置的时候,逆序对增加1,则d[i][j]+=d[i-1][j-1]
...
插入到头部的时候,逆序对增加i-1,则d[i][j]+=d[i-1][j-(i-1)]

那么转移方程为:
d[i][j]=d[i-1][j]+d[i-1][j-1]+...+d[i-1][j-(i-1)]
三层循环i,j,k,这样就有了一个复杂度为O(n*k^2)的算法
观察到n<=1e3,k<=1e3,这样的复杂度不能满足要求,考虑优化

d[i][j]=d[i-1][j]+d[i-1][j-1]+...+d[i-1][j-(i-1)]
d[i][j-1]=d[i-1][j-1]+...+d[i-1][(j-1)-(i-1)]
两者之间有重复计算的部分,因此可推出:
d[i][j]=d[i][j-1]-d[i-1][(j-1)-(i-1)]+d[i-1][j]
这样复杂度就变为O(n*k)了
*/

1012. 至少有 1 位重复的数字(2020.3.30)

题意:

给定正整数 N,返回小于等于 N 且具有至少 1 位重复数字的正整数。

ac:

在这里插入图片描述

code:

class Solution {
public:
    int digit[11];
    int d[11][1<<10][2];
    int dfs(int len,bool limit,bool pre,int sta,bool ok){
        if(!len)return ok;
        if(!limit&&!pre&&d[len][sta][ok]!=-1)return d[len][sta][ok];
        int ans=0;
        int ma=(limit?digit[len]:9);
        for(int i=0;i<=ma;i++){
            if(pre&&i==0){//前导0
                ans+=dfs(len-1,limit&&i==ma,1,sta,ok);
            }else{
                ans+=dfs(len-1,limit&&i==ma,0,sta|(1<<i),ok||(sta)&(1<<i));
            }
        }
        if(!limit&&!pre)d[len][sta][ok]=ans;
        return ans;
    }
    int solve(int n){
        int len=0;
        while(n){
            digit[++len]=n%10;
            n/=10;
        }
        return dfs(len,1,1,0,0);
    }
    int numDupDigitsAtMostN(int N) {
        memset(d,-1,sizeof d);
        return solve(N);
    }
};
/*
数位dp,我也不知道记忆化的时候要记录哪些变量,乱写一下就过了
*/

793. 阶乘函数后K个零(2020.3.31)

题意:

f(x) 是 x! 末尾是0的数量。(回想一下 x! = 1 * 2 * 3 * … * x,且0! = 1)
例如, f(3) = 0 ,因为3! = 6的末尾没有0;而 f(11) = 2 ,因为11!= 39916800末端有2个0。给定 K,找出多少个非负整数x ,有 f(x) = K 的性质。

ac:

在这里插入图片描述

code:

class Solution {
public:
    long long cal(long long n,int x){
        long long ans=0;
        while(n){
            ans+=n/x;
            n/=x;
        }
        return ans;
    }
    long long check(long long mid){
        return min(cal(mid,2),cal(mid,5));
    }
    int preimageSizeFZF(int K) {
        long long l=0,r=1e10;
        long long mi;
        while(l<=r){
            long long mid=(l+r)/2;
            long long t=check(mid);
            if(t>=K)mi=mid,r=mid-1;
            else l=mid+1;
        }
        l=mi,r=1e10;
        long long ma;
        while(l<=r){
            long long mid=(l+r)/2;
            long long t=check(mid);
            if(t>=K+1)ma=mid,r=mid-1;
            else l=mid+1;
        }
        return ma-mi;
    }
};
/*
二分出最小值和最大值即可,注意用longlong
*/

面试题 17.19. 消失的两个数字(2020.4.1)

题意:

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。

code1:

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int sum=0;
        int n=nums.size()+2;
        for(int v:nums)sum+=v;
        int two=n*(n+1)/2-sum;//两个数的和
        n=two/2;//其中一个数肯定是two的一半
        sum=n*(n+1)/2;
        for(int v:nums)if(v<=n)sum-=v;
        return vector<int>{two-sum,sum};
    }
};
/*
n=nums.size()+2;
缺少的两个数的和two=n*(n+1)/2-sum(nums[])
缺少的两个数一定是一个小于等于two/2,一个大于two/2
令k=two/2;
那么计算一遍数组中小于等于k的数的和sum2
那么k*(k+1)-sum2就是其中一个缺少的数p1
那么另外一个数p2=two-p1
*/

code2:


956. 最高的广告牌(2020.4.2)

题意:

你正在安装一个广告牌,并希望它高度最大。这块广告牌将有两个钢制支架,两边各一个。每个钢支架的高度必须相等。
你有一堆可以焊接在一起的钢筋 rods。举个例子,如果钢筋的长度为 1、2 和 3,则可以将它们焊接在一起形成长度为 6 的支架。
返回广告牌的最大可能安装高度。如果没法安装广告牌,请返回 0。

code:

class Solution {
public:
    int tallestBillboard(vector<int>& rods) {
        int len=rods.size();
        int sum=0;
        for(int v:rods)sum+=v;
        int d[2][sum+1];
        int pos=0;
        for(int i=0;i<=sum;i++)d[pos][i]=0;
        for(int i=1;i<=len;i++){
            pos^=1;
            for(int j=0;j<=sum;j++)d[pos][j]=d[pos^1][j];
            int v=rods[i-1];
            for(int j=0;j<=sum;j++){
                if(d[pos^1][j]<j)continue;//差值为j则长度至少为j,否则不成立,跳过
                if(j+v<=sum)d[pos][j+v]=max(d[pos][j+v],d[pos^1][j]+v);
                d[pos][abs(j-v)]=max(d[pos][abs(j-v)],d[pos^1][j]+v);
            }
        }
        return d[pos][0]/2;
    }
};
/*
d[i][j]为前i项差值为j的最大长度,转移方程
d[i][j+v]=max(d[i][j+v],d[i-1][j]+v);
d[i][abs(j-v)]=max(d[i][abs(j-v)],d[i-1][j]+v);
*/

发布了445 篇原创文章 · 获赞 37 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/105087997