leetcode 题解 (500-1000题,持续更新,part 2)

part1(1-500)part3(1000-*)

502. IPO

题意:给定k,w,profits数组和capital数组。k表示最多可完成的任务数。w是初始资本。profits是各个任务的收益。capital是开始此任务需要的初始资金。每次完成一个任务后,将收益加到资本中。问最多可以达到多少资本。
题解:贪心。用优先队列。每次选取可以开始的任务中收益最大的。
class Solution {
public:
    
    struct pc{
        int profits;
        int capital;
        pc(int u,int v):profits(u),capital(v){}

        bool operator < (const Solution::pc &b) const {
            return profits < b.profits;
        }
    };
    struct cmp{
        bool operator()(struct pc &a,struct pc &b)const {
            if(a.capital != b.capital) return a.capital < b.capital;
            return a.profits > b.profits;
        }
    };
    int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) {
        vector<pc> v;
        for(int i = 0;i < Profits.size(); ++i){
            v.push_back(pc(Profits[i],Capital[i]));
        }
        sort(v.begin(),v.end(),cmp());
        
        priority_queue<pc> Q;
        int pos = 0;
        while(pos < v.size() && v[pos].capital <= W){
            Q.push(v[pos++]);
        }
        while(!Q.empty() && k--){
            pc u = Q.top(); Q.pop();
            W += u.profits;
            while(pos < v.size() && v[pos].capital <= W){
                Q.push(v[pos++]);
            }
        }
        
        return W;
    }
};

514. Freedom Trail

题意:给定一个密码环,环上有一些小写字母。通过转动环,然后按按钮输入正确密码才能将门打开。密码是一个字符串。环上的字符以一个字符串给出,按顺时针排列。可以沿着逆时针和顺时针转动,转动一格计一步。按按钮就将位于位置0的字符输入,并计一步。问最少多少步可以将密码输入。
题解:动态规划。设dp[i][j]表示key从i到结尾,环上字符现在位于j时,要输完密码至少得步数。那么要转动到key[i]字符所在位置k,则dp[i][j] = min{dp[i + 1][k]} + 1。而k只要枚举当前位置最接近的左右两个和key[i]相等的位置即可(由于是小写字母,这个可以在O(32 * ring.length())的时间预先计算出来。接着,由于dp[i]只用到dp[i + 1]的信息,所以可以滚动数组,空间复杂度降为O(Ring.size() + Key.size())
class Solution {
public:
    int findRotateSteps(string ring, string key) {
        
        int n = ring.size();
        int m = key.size();
        if(m == 0) return 0;
        vector<vector<int> > left(n,vector<int>(26)),right(n,vector<int>(26));
        
        vector<int> leftpos(26,-1),rightpos(26,-1);
        for(int i = 0;i < n; ++i){
            leftpos[ring[i] - 'a'] = i;
            rightpos[ring[n - i - 1] - 'a'] = n - i - 1;
        }
        
        for(int i = 0;i < n; ++i){
            leftpos[ring[i] - 'a'] = i;
            rightpos[ring[n - i - 1] - 'a'] = n - i - 1;
            for(int j = 0;j < 26; ++j){
                left[i][j] = leftpos[j];
                right[n - i - 1][j] = rightpos[j];
            }
        }
        
        vector<vector<int> > dp(2,vector<int>(n,0));
        
        for(int i = m - 1;i >= 0; --i){
            int c = key[i] - 'a';
            for(int j = 0;j < n; ++j){
                int L = left[j][c];
                int R = right[j][c];
                dp[i & 1][j] = min((j - L + n) % n + dp[1 - i & 1][L],(R - j + n) % n + dp[1 - i & 1][R]) + 1;
            }
        }
        return dp[0][0];
    }
};

517. Super Washing Machines

题意:给定N堆硬币(可能为空),每次可以任意选择m堆,从每一堆中那走一个,放到隔壁堆中取。问至少多少次能使所有堆得硬币一样多。
题解:dp[i]表示前0到i堆至少多少次能把多余的硬币放到右边去,并且除最后一个,都达到了目标值。dp[i + 1] = dp[i] + L +R,其中L和R分别是要从i+1堆向左边和右边拿走的硬币数量。
class Solution {
public:
    int findMinMoves(vector<int>& machines) {
        int n = machines.size();
        if(n <= 1) return 0;
        int tot = 0,ans = 0;
        for(int i = 0;i < n; ++i) tot += machines[i];
        if(tot % n) return -1;
        int ave = tot / n;
        machines.push_back(ave);
        tot = 0;
        tot = machines[0] - ave;
        
        for(int i = 0;i < n; ++i){
            if(tot >= 0)
                ans = max(tot,ans);
            else{
                ans = max(ans,max(0,machines[i + 1] + tot - ave) - tot);
            }
            tot += machines[i + 1] - ave;
            machines[i + 1] += tot;
        }
        return ans;
    }
};

546. Remove Boxes

题意:给定一个整数序列,每个数字表示颜色。每次可以取走若干个连续的同颜色的一段(长度为k),获得k*k的分数。问怎么取可以使得当序列取完获得的分数最大。
题解:动态规划。因为连续的肯定是要一起取走的,所以先合并。设dp[i][j][k]表示从位置i到位置j,前面剩下有k个和i相同颜色的位置,那么要么i和前面的合并掉取走,要么一起留下来,和下一个一样的合并。答案是dp[0][n - 1][0]。
//vector会超内存
class Solution {
public:
    
    void merge(vector<int>& boxes,int n,vector<int>&color,vector<int>&length){
        boxes.push_back(-1);
        int curLength = 0;
        for(int i = 0;i < n; ++i){
            ++curLength;
            if(boxes[i] == boxes[i + 1]) continue;
            length.push_back(curLength);
            color.push_back(boxes[i]);
            curLength = 0;
        }
    }
    
    int removeBoxes(vector<int>& boxes) {
        int n = boxes.size();
        if(n <= 1) return n;
        vector<int> color,length;
        
        merge(boxes,n,color,length);
        
        n = color.size();
        if(n == 1) return length[0] * length[0];
        //vector<vector<vector<int>>> dp(n,vector<vector<int>>(n,vector<int>(boxes.size(),0)));
        
        int dp[100][100][100] = {0};
        
        return dfs(dp,color,length,0,n - 1,0);
    }
    
     int dfs(int dp[100][100][100],const vector<int>& color,vector<int>&length,int i,int j,int k){
        if(i > j) return 0;
        if(i == j) return (k + length[i]) * (k + length[i]);
        int &res = dp[i][j][k];
        if(res) return res;
        
        res = dfs(dp,color,length,i + 1,j,0) + (k + length[i]) * (k + length[i]);
        for(int p = i + 1;p <= j; ++p){
            if(color[p] != color[i]) continue;
            res = max(res, dfs(dp, color,length, i + 1, p - 1, 0) + dfs(dp, color,length, p, j, k + length[i]));
        }
        
        
        return res;
    }
    
    
};

552. Student Attendance Record II

题意:给定三种字符(A,P,L)给定一个数字n,问长度n由这三种字符组成且的串的共有多少个。限制是其中一种只能有一个(A),还有一种最多两个连续(L),剩下一个没有限制(P)。
题解:dp[i][j][k]表示长度为i,A是否出现过,结尾连续L的个数为k的串的个数。然后进行转移即可。由于dp[i]只用到dp[i - 1]的信息,故可以用滚动数组。
class Solution {
public:
    const long long MOD = 1E9 + 7;
    int checkRecord(int n) {
        long long dp[2][2][3] = {0LL}; 
        
        dp[0][0][0] = 1LL;

        for(int i = 1;i <= n; ++i){
            for(int k = 0;k < 3; ++k){
                if(k > 0){
                    dp[i&1][0][k] = dp[1 - i&1][0][k - 1];
                    dp[i&1][1][k] = dp[1 - i&1][1][k - 1];
                }
                else{
                    dp[i&1][0][k] = dp[1 - i&1][0][0] + dp[1 - i&1][0][1] + dp[1 - i&1][0][2];
                    dp[i&1][1][k] = dp[1 - i&1][1][0] + dp[1 - i&1][1][1] + dp[1 - i&1][1][2] +
                                  dp[i&1][0][k];
                }
                dp[i&1][0][k] %= MOD; 
                dp[i&1][1][k] %= MOD;
            }
            
        }
       return (dp[n&1][0][0] + dp[n&1][0][1] + dp[n&1][0][2] + dp[n&1][1][0] + dp[n&1][1][1] + dp[n&1][1][2]) % MOD; 
    }
};

564. Find the Closest Palindrome

题意:给定一个数字字符串,确定和它离最近的那个回文串,如果有两个一样近,输出小的那个。
题解:取这个字符串的前半段,如果它本身不是回文串,那么取前半段然后镜像构造一个回文串,这个肯定是距离最小的。如果它本身是回文串,将前半段减1,加1两种情况构造回文串。最近那个肯定是这两个中的一个,这个很显然,接着就是细节的问题了。
class Solution {
public:
    
    bool g(string a,string b){ //a > b? true: false;
        if(a == "inf") return true;
        if(b == "inf") return false;
        if(a.length() > b.length()) return true;
        if(a.length() < b.length()) return false;
        
        for(int i = 0;i < a.length(); ++i){
            if(a[i] > b[i]) return true;
            if(a[i] < b[i]) return false;
        }
        return false;
    }
    
    string dis(string a,string b){
        if(a == b) return "inf";
        if(g(a,b)) swap(a,b);
        a = string(b.length() - a.length(),'0') + a;
        
        char tmp = 0;
        string s = "";
        int i = 0;
        for(int i = a.length() - 1;i >= 0; --i){
            char t = b[i] - a[i] - tmp; 
            if(t < 0) tmp = 1, t += 10;
            else tmp = 0;
            s = char('0' + t) + s;
        }
        int pos = 0 ;
        while(pos < s.length() && s[pos] == '0') ++pos;
        s = s.substr(pos);
        
        return s == string("0") ? "inf":s;
    }
    string minor(string s){
        int mid = (s.length() + 1) / 2;
        for(int i = mid; i < s.length() ; ++i)
            s[i] = s[s.length() - 1 - i];
        return s;
    }
    string nearestPalindromic(string s) {
        int pos = 0;
        while(pos < s.length() - 1 && s[pos] == '0') ++pos;
        s = s.substr(pos);
        int n = s.size();
        
        if(n <= 1){
            cout<<"true"<<endl;
            if(s == "") return "0";
            if(s == "0") return "1";
            cout<<"noew"<<(s[0] - 1)<<endl;
            return string("") + char(s[0] - 1);
        }
        
        string s1 = minor(s);
        string diff1 = dis(s,s1);
        int mid = (s.length() - 1) / 2;
        string s2 = s;
        while(mid >= 0 && s2[mid] == '0'){
            s2[mid] = '9';
            --mid;
        }
        if(mid == 0 && *(s2.begin()) == '1'){
            s2.erase(s2.begin());
            s2[(s2.length() - 1) / 2] = '9';   //this is because it's the closer one
        }
        else
            s2[mid] = s2[mid] - 1;
        s2 = minor(s2);
        string diff2 = dis(s,s2);
        
        mid = (s.length() - 1) / 2;
        
        string s3 = s;
        while(mid >= 0 && s3[mid] == '9'){
            s3[mid] = '0';
            --mid;
        }
        if(mid < 0)
            s3 = "1" + s3;
        else
            s3[mid] = s3[mid] + 1;
        
        s3 = minor(s3);
        
        string diff3 = dis(s,s3);
        
        if(!g(diff2,diff1) && !g(diff2,diff3)) return s2;
        if(!g(diff1,diff2) && !g(diff1,diff3)) return s1;
        return s3;
        
    }
};

587. Erect the Fence

题意:就是求凸包,都是整点,可能有重复,也可以退化成一直线。从最左边开始,逆时针输出。
题解:Graham扫描
/**
 * Definition for a point.
 * struct Point {
 *     int x;
 *     int y;
 *     Point() : x(0), y(0) {}
 *     Point(int a, int b) : x(a), y(b) {}
 * };
 */
Point operator-(Point &a, Point &b) {
        int x = a.x - b.x;
        int y = a.y - b.y;
        return Point(x,y);
}
class Solution {
public:
    
    
    struct cmp{
        bool operator()(const Point &a, const Point &b){
            if(a.x == b.x) return a.y < b.y;
            return a.x < b.x;
        }
    };
    bool eq(const Point &a, const Point &b){
        return a.x == b.x && a.y == b.y;
    }
    
    int cross(Point &a,Point &b){
        return a.x * b.y - a.y * b.x;
    }
   
    
    vector<int> convexHull(vector<Point>& points){
        
        
        int n = points.size();
        vector<int> S(n,0);
        int end = 0;
        
        for(int i = 1;i < n; ++i){
            if(end == 0){
                S[++end] = i;
                continue;
            }
            int top = S[end];
            int pre = S[end - 1];
            Point v1 = points[i] - points[pre];
            Point v2 = points[top] - points[pre];
            if(cross(v1,v2) > 0){
                --end;
                --i;
            }else{
                S[++end] = i;
            }
        }
        vector<int> down(S.begin(),S.begin() + end + 1);
        
        if(down.size() == points.size()) return down;
        
        S[0] = n - 1; end = 0;
        for(int i = n - 2;i >= 0; --i){
            if(end == 0){
                S[++end] = i;
                continue;
            }
            int top = S[end];
            int pre = S[end - 1];
            Point v1 = points[i] - points[pre];
            Point v2 = points[top] - points[pre];
            if(cross(v1,v2) > 0){
                --end;
                ++i;
            }else{
                S[++end] = i;
            }
        }
        
        vector<int>up(S.begin() + 1,S.begin() + end);
        
        down.insert(down.end(),up.begin(),up.end());
        return down;
    }
    /*
    int gcd(int a,int b){
        return b == 0 ? a : gcd(b, a % b);
    }
    
    bool inaLine(vector<Point>& points){
        int pos = 1;
        while(pos < points.size() && points[pos].x == points[0].x && points[pos].y == points[0].y) ++pos;
        if(pos == points.size()) return true;
        
        Point tmp = points[pos] - points[0];
        
        int x = tmp.x,y = tmp.y;
        int d = gcd(x,y);
        x /= d; y /= d;
        for(int i = 1;i < points.size(); ++i){
            int px = points[i].x - points[0].x, py = points[i].y - points[0].y;
            if(px == 0 && py == 0) continue;
            d = gcd(px,py);
            px /= d; py /= d;
            if(x != px || y != py) return false;
        }
        
        
        return true;
    }
    */
    vector<Point> outerTrees(vector<Point>& points) {
        if(points.size() <= 1) return points;
        
        sort(points.begin(),points.end(),cmp()); //left to right ,down to up
        //if(inaLine(points)) return points;
        vector<int> convex = convexHull(points);
        vector<Point> ans;
        for(auto pos:convex) ans.push_back(points[pos]);
        return ans;
    }
};

591. Tag Validator

题意:验证一个标签是否合法。

标签以<tagname>起始</tagname终止>,tagname必须是大写字符,且长度不大于9。

形式如下<tagname>tagcontent </tagname>

tagcontent可能包含以下部分,

  1. 一般text(不包含左尖括号<)
  2. CDATA形式为  <![CDATA[CDATA_CONTENT]]>,CDATA_CONTENT可以为任意字符
  3. tag嵌套

题解:模拟题。直接模拟判断过程。懒得写,见代码注释

class Solution {
public:
    bool isValid(string code) {
		if(code.empty() || code[0] != '<')
			return false;
		int index = 0, N = code.length();
        stack<string> tag;
        
		while(index < N){
            //遇到左括号,三种情况,起始,终止标签和CDATA
			if(code[index] == '<'){
				++index;
                //CDATA部分
				if(index < N && code[index]== '!'){
                    //不被包含在tag中,则invalid
                    if(tag.empty())
                        return false;
					++index;
                    //接下是[CDATA[
					if(code.substr(index, 7) != "[CDATA[")
						return false;
					index += 7;
                    //CDATA_CONTENT
                    while(true){
                        //找到一个可能的CDATA_CONTENT结尾
						while(index < N && code[index] != ']') ++index;
						if(index < N - 3){
							if(code.substr(index,3) == "]]>")  break;
						}else
							return false;
						++index;
					}
				}else if(index < N && code[index]== '/'){ //tag结尾
					if(tag.empty())
						return false;
					++index;
					int i = index, len;
                    //标签,len为标签长度
					while(i < N && code[i] != '>'){
						len = i - index + 1;
						if(len > (int)tag.top().length() || code[i] != tag.top()[len - 1])
							return false;
						i++;
					}
					len = i - index;
					if(len != (int)tag.top().length())
						return false;
                    
                    
					tag.pop();
					index = ++i;
                    //外层必须是一个tag,而不是两个tag并排
                    if(tag.empty() && index != N)
                        return false;
				}else{ //起始标签
					int i = index, len;
                    //tag
					while(i < N && code[i] != '>'){
						len = i - index + 1;
    
						if(code[i] > 'Z' || code[i] < 'A')
							return false;
						i++;
						if(len > 9)
							return false;
					}
					len = i - index;
					if(i == N || len < 1 || len > 9)
						return false;
					tag.push(code.substr(index,len));
					index = ++i;
				}
			}else //其他内容。直接略
				++index;
		}
		return tag.empty();
    }
};

 

 

600. Non-negative Integers without Consecutive Ones

题意:给定一个数字n,问比它小的,且二进制没有连续1的非负数有多少个。
题解:注意到,二进制长度为k的没有连续1的非负数的是Fibonacci数。接着,对于一个n,我们分别计算前缀和它一样的那些数有多少,加起来即可。如果它本身也是,加上它本身。另外一种做法是动态规划。dp[i][j]表示前i位,最后一位为j且不大于num前缀的数目。
class Solution {
public:
    
    int findIntegers(int num) {
        int fib[31];
        fib[0] = 1;
        fib[1] = 2;
        for (int i = 2; i < 32; ++i)
            fib[i] = fib[i - 1] + fib[i - 2];
        int ans = 0, k = 30, pre_bit = 0;
        for(int k = 30;k >= 0; --k) {
            if (num & (1 << k)) {
                ans += fib[k];
                if (pre_bit) return ans;
                pre_bit = 1;
            }else
                pre_bit = 0;
        }
        return ans + 1;
    }
};
动态规划解法,压缩空间:
public class Solution {
    public int findIntegers(int num) {
        //one:all bit before cur is less than num and no continues 1 and cur bit is at one;
        //zero:all bit before cur is less than num and no continues 1 and cur bit is at zero;
        int one=0,zero=0,temp;
        boolean isNum=true;
        for(int i=31;i>=0;i--){
            temp=zero;
            zero+=one;
            one=temp;
            if(isNum&&((num>>i)&1)==1){
                zero+=1;
            }
            if(((num>>i)&1)==1&&((num>>i+1)&1)==1){
                isNum=false;
            }
        }
        return one+zero+(isNum?1:0);
    }
}

629. K Inverse Pairs Array

题意:给定n,k。问由1 ~n组成的序列中逆序对为k的共有多少种情况
题解:动态规划。设dp[i][j]为,前面有1~i + 1的序列,逆序对为j的有多少种。那么dp[i][j]
可以由i + 1所在位置进行递推。dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] + ... dp[i - 1][max(0,j - i)]
和每次预先计算,故时间复杂度为O(nk)
class Solution {
public:
    const int MOD = 1E9 + 7;
    int getsum(int i,int j, vector<int> &sum)const {
        int pre = max(j - i,0) - 1;
        if(pre < 0) return sum[j];
        else return sum[j] - sum[pre];
    }
    
    int kInversePairs(int n, int k) {
        if(k == 0) return 1;
        vector<int> dp(k + 1,0);
        vector<int> sum(k + 1,1);
        
        dp[0] = 1;
        
        for(int i= 0;i < n; ++i){
            for(int j = 1;j <= k; ++j){
                dp[j] = (getsum(i,j,sum) % MOD + MOD) % MOD;
            }
            for(int j = 1;j <= k; ++j) sum[j] = (sum[j - 1] + dp[j]) % MOD;
        }
        
        return dp[k];
    }
};

630. Course Schedule III

题意:给定一些课程,以(需要的时间,最迟结束时间)给出。课程之间不能重叠。问最多可以安排多少个课程。
题解:贪心。先对课程,按最迟结束时间从小到大排序。然后一个个安排。如果可以安排下,那么就放进去。如果安排不下,那么去掉前面(包括自己)一个耗时最多的。这样子肯定是最优的。因为对于前i个,如果这样做事最优的,那么对于第i+1个,这样做也肯定是最优的,不然得出矛盾。
class Solution {
public:
    struct cmp{
        bool operator ()(vector<int> &a, vector<int>&b){
            return a[1] < b[1];
        }
    };
    struct cmp2{
        bool operator ()(pair<int,int> &a,pair<int,int> &b){
          
               return a.first < b.first;
            
        }
    };
    
    int scheduleCourse(vector<vector<int>>& courses) {
        
        sort(courses.begin(),courses.end(),cmp());
        priority_queue<pair<int,int> ,vector<pair<int,int>>, cmp2>Q;
        int endTime = 0;
        for(int i = 0;i < courses.size(); ++i){
            Q.push(make_pair(courses[i][0],courses[i][1]));
            endTime += courses[i][0];
            if(endTime > courses[i][1]){
                endTime -= Q.top().first;
                Q.pop();
            }
        }
        return Q.size();
    }
};

632. Smallest Range

题意:给定k个list,每个list是一个有序int数组。求一个最小的区间,使得这k个数组中每一个都至少存在一个数在这个区间中。
题解:先合并成一个pair数组(值和所属的list)。然后双指针遍历即可。
class Solution {
public:
    static bool cmp(pair<int,int> &a,pair<int,int> &b){
        return a.first > b.first;
    }
    vector<int> smallestRange(vector<vector<int>>& nums) {
        vector<pair<int,int>> numpair;
        
        int k = nums.size();
        for(int i = 0;i < k; ++i){
            vector<pair<int,int>> tmp;
            for(auto &num:nums[i]){
                tmp.push_back(make_pair(num,i));
            }
            numpair.resize(numpair.size() + tmp.size());
            merge(numpair.rbegin() + tmp.size(),numpair.rend(),tmp.rbegin(),tmp.rend(),numpair.rbegin(),cmp);
        }
        
        vector<int> counts(k);
        int visn = 0;
        int a = -1,b = -1,minimumS = INT_MAX;
        int pre = 0;
        for(int i = 0;i < numpair.size() ; ++i){
            if(++counts[numpair[i].second] == 1) ++visn;
            
            if(visn == k){
                while(counts[numpair[pre].second] > 1){
                    --counts[numpair[pre].second];
                    ++pre;
                }
                if(numpair[i].first - numpair[pre].first < minimumS){
                    minimumS = numpair[i].first - numpair[pre].first; 
                    a = numpair[pre].first;
                    b = numpair[i].first;
                }
            }
        }
     return  vector<int>{a,b};   
    }
};

639. Decode Ways II

题意:字符A-Z能编码成1到26,给定一串编码,问可以解码成多少种情况。其中编码;*;可以代表1-9中任意字符。
题解:动态规划。dp[i]表示编码串前i个可以解码的种数。然后按当前字符和前一个字符的类别进行状态转移。

class Solution {
public:
    long long M = 1000000007;
    int numDecodings(string s) {
        int n = s.size();
        if(n == 0) return 1;
        vector<long long> dp(n + 1,0);
        dp[0] = 1;
        dp[1] = s[0] == '*'? 9 : s[0] != '0';
        if(dp[1] == 0) return 0;
        
        for(int i = 2; i <= n; ++i){
            if(s[i - 2] == '1'){
                if(s[i - 1] == '0') dp[i] = dp[i - 2];
                else if(s[i - 1] == '*') dp[i] = 9 * dp[i - 2] + dp[i - 1] * 9;
                else dp[i] = dp[i - 2] + dp[i - 1];
                
            }else if(s[i - 2] == '2'){
                if(s[i - 1] == '0') dp[i] = dp[i - 2];
                else if(s[i - 1] == '*') dp[i] = 6 * dp[i - 2] + dp[i - 1] * 9;
                else if(s[i - 1] <= '6' && s[i - 1] >='1') dp[i] = dp[i - 2] + dp[i - 1];
                else dp[i] = dp[i - 1];
                
            }else if(s[i - 2] == '*'){
                if(s[i - 1] == '0') dp[i] = 2 * dp[i - 2]; 
                else if(s[i - 1] == '*') dp[i] = 15 * dp[i - 2] + dp[i - 1] * 9;
                else if(s[i - 1] <= '6' && s[i - 1] >= '1') dp[i] = 2 * dp[i - 2] + dp[i - 1];
                else dp[i] = dp[i - 2] + dp[i - 1];
                
            }else if(s[i - 2] > '2'){ //s[i - 2] greater than 2
                if(s[i - 1] == '0') dp[i] = 0;
                else if(s[i - 1] == '*')
                    dp[i] = 9 * dp[i - 1];
                else
                    dp[i] = dp[i - 1];
            }else{ //0
                if(s[i - 1] == '0') dp[i] = 0;
                else dp[i] = s[i - 1] == '*'? 9 * dp[i - 1] :  dp[i - 1];
            }
            dp[i] = dp[i] % M;
            
        }
        return dp[n];
        
    }
};

644. Maximum Average Subarray II

题意:给定一个数组和一个数字k。问长度至少为k的连续段的最大平均值为多少。
题解:一个简单的做法就是二分答案,然后check。
 (nums[i]+nums[i+1]+...+nums[j])/(j-i+1)>=x
=>nums[i]+nums[i+1]+...+nums[j]>=x*(j-i+1)
=>(nums[i]-x)+(nums[i+1]-x)+...+(nums[j]-x)>=0
相当于检查有没有连续段长度至少为k且和非负。也就是最大k子段和,用单调队列动态规划解决。
算法复杂度为O(nlogn)。但这不是最优的,利用计算几何的方法,可以达到O(n)。
首先,计算前缀和P[i]。然后i到j的平均值为(P[j] - P[i - 1]) / (j - i + 1)。
我们把它看成两个点(i - 1,P[i - 1]),(j,P[j]),显然平均值就是他们两个点的线段斜率。
接着。我们从左至右,维护一个下凸包,每次就是找凸包的下切线。具体图解见:
http://www.csie.ntnu.edu.tw/~u91029/MaximumSubarray.html
该问题的一个增强版本是加上限宽度。也就是在单调队列里面加一个操作即可

class Solution {
public:
    /*
    bool check(vector<int>& nums, int k,double sum){
        double cur = 0,pre = 0;
       
        for(int i = 0;i < nums.size(); ++i){
            cur += nums[i] - sum;
            if(i >= k) pre += nums[i - k] - sum;
            if(pre < 0) cur -= pre, pre = 0;
            if(i >= k - 1 && cur >= 0) return true;
        }
        return cur >= 0;
    }
    
    double binarySearchSolution(vector<int>& nums, int k){
        double eps = 1e-6;
        double left = INT_MIN,right = INT_MAX,mid;
        while(right - left > eps){
            mid = (left + right) / 2;
            if(check(nums,k,mid)) left = mid;
            else right = mid;
        }
        return right;
    }
    */
    
    double slope(int x,int y, vector<int> &presum){ // the slope of the x,y
        return double(presum[y + 1] - presum[x]) / (y + 1 - x);
    }
        
    
    double convexHullSolution(vector<int>& nums, int k){
        deque<int> Q;
        vector<int> presum(nums.size() + 1);
        presum[0] = 0;
        double ans = INT_MIN;
        for(int i = 0;i < nums.size(); ++i)  presum[i + 1] = presum[i] + nums[i];
        for(int i = k - 1;i < nums.size(); ++i){
            while(Q.size() > 1 && slope(*(Q.end() - 2),Q.back() - 1,presum)  >= slope(*(Q.end() - 2),i - k,presum)) Q.pop_back(); //update the convex
            Q.push_back(i - k + 1);
            while(Q.size() > 1 && slope(Q.front(),Q.at(1) - 1,presum)  <= slope(Q.front(),i,presum)) Q.pop_front();
            ans = max(ans,slope(Q.front(),i,presum));
            
        }
        return ans;
    }
     
    double findMaxAverage(vector<int>& nums, int k) {
        //return binarySearchSolution(nums,k);
        return convexHullSolution(nums,k);
    }
};

664. Strange Printer

题意:有一个stange printer他能做以下的事

  1. 每次只能从一个位置到另一个位置打印同一个字符
  2. 在每一轮的打印中,新打印的会覆盖旧打印的字符

给定一个字符串,问至少需要多少次打印才能得到。比如aba需要两次。先打印aaa,再打印b

题解:动态规划,设dp[i][j]表示i到j子串需要多少次打印。则有以下的情况

首先注意到,第一个字符一定可以在第一轮打印。如果它单独打印显然可以,否则,它和到另外的一个位置k的子串打印,那么显然也可以放在第一次,因为如果不是第一次,那么会覆盖掉前面的打印,把前面的打印的起始位置移到k以后并不会产生差别。所以我们可以分两种情况

  1. i位置单独打印,则次数为dp[i + 1][j] + 1
  2. i和k一起打印(s[i] == s[k]),那么k这个位置就不用管了(相当于和i合一起了),并且将串分成两部分i  ~ k , k + 1~ j。i~k的打印次数为dp[i][k - 1],k+1~j的打印次数dp[k + 1][j]。总次数为dp[i][k - 1] + dp[k + 1][j]
class Solution {
    int dp[101][101];
    
    int dfs(int i, int j, string &str){
        if(i > j) return 0;
        if(i == j) return 1;
        if(dp[i][j]) return dp[i][j];
        
        dp[i][j] = dfs(i + 1, j, str) + 1;
        for(int k = i + 1; k < str.length(); ++k){
            if(str[k] == str[i]) dp[i][j] = min(dp[i][j], dfs(i, k - 1, str) + dfs(k + 1, j, str));
        }
            
        return dp[i][j];
    }
    
public:
    int strangePrinter(string s) {
        string str = "";
        for(int i = 0;i < s.length(); ++i){
            str += s[i];
            while(i + 1 < s.length() && s[i + 1] == s[i]) ++i;
        }
        return dfs(0, str.length() - 1, str);
    }
};

668. Kth Smallest Number in Multiplication Table

题意:给定一个m *n的乘法表,求里面第k大的数

题解:二分答案mid,然后判断小于等于mid的有多少对。这个通过枚举第一个乘数就知道了。假设乘数为i,则其中小于等于mid的数为min(n, mid / i)个。(用双指针可能更快点,因为除法比较耗时,而++运算--运算是很快的)

class Solution {
public:
    int findKthNumber(int m, int n, int k) {
        
        int left = 1, right = n * m;
        
        while(left <= right){
            int mid = (left + right) >> 1;
            
            int num = (mid / n) * n;
            for(int i = mid / n + 1; i <= min(m, mid); ++i){
                num += mid / i;
            }
            
            if(num < k){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return right + 1;
    }
};

675. Cut Off Trees for Golf Event

题意:给以个网格,表示一个森林,其中0表示障碍物不能通过,其他都能通过,1表示平地,高于1表示有一棵树。从(0,0)位置开始,每次砍最低的树,最少需要多少步

题解:按树高排序,就是从当前位置走到下个树的高度位置的最少步数,bfs可解决。一开始我看成树是不能通过的,只有砍掉才能通过,这样也可以做,稍微修改即可(将排序部分改成优先队列)。

class Solution {
    typedef pair<int, pair<int,int>> height_pos, step_pos;
    //放在外面会快很多
    bool vis[50][50];
    queue<step_pos> Q;
    
    int get_step(const pair<int,int> &s, const pair<int,int> &e, const vector<vector<int>>& forest){
        while(not Q.empty()) Q.pop();
        Q.push(make_pair(0, s));
        //vector<vector<bool>> vis(forest.size(), vector<bool>(forest[0].size(), false));
        memset(vis, 0, sizeof(vis));
        
        int dir_x[] = {1, 0, -1, 0};
        int dir_y[] = {0, 1, 0, -1};
        vis[s.first][s.second] = true;
        while(not Q.empty()){
            step_pos cur = Q.front(); Q.pop();
            int x = cur.second.first, y = cur.second.second;
            if(cur.second == e) return cur.first;
            
            for(int d = 0; d < 4; ++d){
                int next_x = x + dir_x[d];
                int next_y = y + dir_y[d];
                if(next_x < 0 or next_y < 0 or next_x >= forest.siz
### Windows 上设置和配置路由转发 #### 关闭防火墙并允许必要的网络流量 为了确保路由功能正常工作,建议先关闭防火墙或将特定端口和服务设为例外。这一步骤可以防止不必要的安全策略阻止数据包通过。 #### 开启IP路由功能 在Windows Server环境中启用IP路由需要修改注册表项或使用命令行工具来激活该特性: - **方法一:通过命令提示符** 使用具有管理员权限的命令提示符执行如下指令以开启IP路由: ```batch reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v IPEnableRouter /t REG_DWORD /d 1 /f ``` - **方法二:手动编辑注册表** 可以直接打开注册表编辑器导航到`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters`路径下创建名为`IPEnableRouter`的新DWORD(32)值,并将其数值设定为`1`[^3]。 #### 添加静态路由条目 完成上述操作之后,还需要向系统中添加具体的路由规则以便正确地指导进出的数据流方向。例如,如果要使来自子网10.6.0.0/24的所有通信都经由网关10.1.0.120传输,则应运行以下命令: ```batch route add -p 10.6.0.0 mask 255.255.255.0 10.1.0.120 ``` 此命令中的参数解释如下:“-p”表示永久保存这条记录;“mask”后面跟着的是子网掩码地址;最后一个部分则是下一跳路由器即默认网关的位置[^2]。 #### 测试与验证 最后,在完成了以上所有步骤后应当测试新建立起来的连接是否能够按照预期发挥作用。可以通过尝试从不同设备访问互联网资源或者利用ping命令来进行简单的连通性检测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值