LeetCode 题解(1000-*, 持续更新,part3)

第一部分,编号500以内的部分
第二部分,编号501-1000部分
本博客不再更新,后面的部分见我写的另一个博客 myblog

这是编号1000以上的部分(加锁部分暂时买不起,后面有机会开个会员)

  1. Grid Illumination

题意:给定一个N,表示有N*N的网格(下标0到N-1),网格中有一些点放置有灯。每个灯能够照亮所在的行列以及两条对角线。
给定一些查询,每个查询是一个点,首先先确定位置是否被照亮,如果是,那么周围3*3方格内的灯都灭掉。接着查询下一个。
需要返回每个查询位置是否被照亮,保存为一个数组

题解:首先用一个set保存灯所在的点,接着我们可以保存被照亮的行,列和两个对角线,行和列直接用坐标就可以表示。而对角线我们取它和x轴的交点,分别是x - y 和x + y。接着就是在上面做查询和删除操作了。

class Solution {
   
    static const int MOD = 100007;
    struct pair_hash {
   
        inline size_t operator()(const pair<int,int> & p) const {
   
            return (p.first % MOD * 1000 + p.second) % MOD;
        }
    };
    
public:
    vector<int> gridIllumination(int N, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
   
        unordered_set<pair<int, int>, pair_hash> lamps_pos;
        unordered_map<int, int> ill_pos[4];
        for(int i = 0;i < lamps.size(); ++i){
   
            int x = lamps[i][0], y = lamps[i][1];
            lamps_pos.insert(make_pair(x, y));
            ++ill_pos[0][x];
            ++ill_pos[1][y];
            ++ill_pos[2][x - y];
            ++ill_pos[3][x + y];
        }
        vector<int> ans;
        
        for(int i = 0;i < queries.size(); ++i){
   
            int x = queries[i][0], y = queries[i][1];
            
            if(ill_pos[0][x] || ill_pos[1][y] || ill_pos[2][x - y] || ill_pos[3][x + y])
                ans.push_back(1);
            else 
                ans.push_back(0);
            
            for(int j = -1; j <= 1; ++j){
   
                for(int k = -1; k <= 1; ++k){
   
                    int cur_x = x + j;
                    int cur_y = y + k;
                    if(cur_x < 0 || cur_y < 0 || cur_x >= N || cur_y >= N) continue;
                    if(lamps_pos.count(make_pair(cur_x, cur_y))){
   
                        lamps_pos.erase(make_pair(cur_x, cur_y));
                        --ill_pos[0][cur_x];
                        --ill_pos[1][cur_y];
                        --ill_pos[2][cur_x - cur_y];
                        --ill_pos[3][cur_x + cur_y];
                    }
                }
            }
        }
        return ans;
    }
};
  1. Numbers With Repeated Digits

题意:给定一个数字N,问不大于N的正整数有多少个里面具有重复数字的

题解:重复的难求,我们求不重复的,然后总数N + 1个减去不重复的
不重复的我们可以通过逐位计算。不过有另外的解法,稍慢一点,就是动态规划,见代码注释。

int dp[10][1024];

class Solution {
   
public:
    int numDupDigitsAtMostN(int N) {
   
        if (N < 10) return 0;
        string str = to_string(N);
        int numDigits = str.length();        
        memset(dp, -1, sizeof dp);
        
        return N - helper(0, 0, 1, str) + 1;
    }
    
    //dp[index][d]表示index位的数,位标记为d的不重复数字数目
    //helper计算的是不超过N的有多少
    //tight表示前缀固定,和N的前缀一样
    int helper(int index, int d, int tight, string& str)
    {
   
        if (index == str.length()) return 1;        
        if (tight != 1 && dp[index][d]  != -1) return dp[index][d];
        int res  = 0;
        int range = tight == 1 ? str[index] - '0' : 9;
        for (int i = 0 ; i <= range; i++)
        {
   
            if (d & (1 << i)) continue;
            int newTight = 0;
            
            if (tight == 1 && (str[index] - '0') == i) newTight = 1;
            //前导0不能做标记,因为可以重复出现
            if (d == 0 && i == 0) res += helper(index + 1, d, newTight, str);
            else res += helper(index + 1, d | (1 << i), newTight, str);
        } 
		if (!tight) dp[index][d] = res;
        return res;
    }
};
  1. Camelcase Matching

搞错了,这是个中等题,难怪这么简单。
题意:给定一些query串和一个pattern。一个query串能和pattern匹配如果pattern能够通过插入一些小写字母得到query串。
返回一个bool数组,表示每个query是否和pattern匹配

题解:贪心匹配。每个query如果能匹配完pattern,且其他字符都是小写字母就算匹配成功。

class Solution {
   
public:
    vector<bool> camelMatch(vector<string>& queries, string pattern) {
   
        vector<bool> ans;
        
        for(int i = 0;i < queries.size(); ++i){
   
            
            string &cur_q = queries[i];
            int pos = 0;
            int match = true;
            
            for(int j = 0; j < cur_q.length(); ++j){
   
                if(pos < pattern.length() && cur_q[j] == pattern[pos]){
   
                    ++pos;
                    continue;
                }
                if('a' <= cur_q[j] && cur_q[j] <= 'z') continue;
                match = false;
                break;
            }
            ans.push_back(match && pos == pattern.size());
        }
        return ans;
    }
};
  1. Stream of Characters

题意:给定一些words(字符串),构造一个数据结构可以支持一下查询。每次查询一个字符。如果倒数k(k>=1)个查询的字符组成的字符串在words中出现,则返回true,否则返回false

题解:几乎是AC自动机的模板题。构建AC自动机。记录到当前字符为止,匹配的最长位置。然后如果当前位置是一个单词结尾返回true,否则如果last指针指向的位置不是0,则说明有后缀可以匹配到字符串,返回true。
当然,用逆序字符串构建Trie也可以过。

class AAM{
   
    static const int N = 300000;
    int ch[N][26];
    int fail[N],last[N],vis[N];
    bool val[N];
    int cnt,root;
    
    int p; //当前到达的节点(最长匹配)
public:

    void init(){
   
       cnt = 0, p = root = cnt++;
       memset(vis,0,sizeof(vis));
       memset(ch[0],0,sizeof(ch[0]));
       memset(val,0,sizeof(val));
        
    }
    
    void insert(string s){
   
            int len = s.length();
            int p = root;
            for(int i = 0; i < len; ++i){
   
                    int index = s[i] - 'a';
                    if(!ch[p][index]){
   
                            memset(ch[cnt],0,sizeof(ch[cnt]));
                            ch[p][index] = cnt++;
                    }
                    p = ch[p][index];
            }
            val[p] = true;
    }
    
    void getfail(){
   
            int p = root;
            queue<int>Q;
            fail[p] = last[p] = p;
            for(int i = 0;i < 26; ++i){
   
                int u = ch[p][i];
                if(u) {
   last[u] = fail[u] = p;Q.push(u);}
                else ch[p][i] = p;
            }
            while(!Q.empty()){
   
                int p = Q.front(); Q.pop();
                for(int i = 0; i < 26; ++i){
   
                        int &u = ch[p][i];
                        if(u) {
   
                                fail[u] = ch[fail[p]][i];
                                last[u] = val[fail[u]]?fail[u]:last[fail[u]];
                                Q.push(u);
                        }else u = ch[fail[p]][i];
                }
            }
    }
   
    bool find(char c){
   
        p = ch[p][c - 'a'];
        return val[p] || last[p];
    }
   
};
class StreamChecker {
   
    AAM ac_m;
public:
    StreamChecker(vector<string>& words) {
   
        ac_m.init();
        for(int i = 0;i < words.size(); ++i)
            ac_m.insert(words[i]);
        
        ac_m.getfail();
    }
    
    bool query(char letter) {
   
        return ac_m.find(letter);
    }
};

/**
 * Your StreamChecker object will be instantiated and called as such:
 * StreamChecker* obj = new StreamChecker(words);
 * bool param_1 = obj->query(letter)
*/
 
 
  1. Recover a Tree From Preorder Traversal

题意:给定一个字符串,表示一颗二叉树的前序遍历顺序,如下例,其中数字是节点值,横杠表示深度。而且如果一个节点只有一个儿子,那么必定是左儿子。

例子
Input: “1-2–3--4-5–6--7”
Output: [1,2,5,3,4,6,7]

题解:用一个栈恢复遍历过程即可。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */




class Solution {
   
    
    int get_val(const string &s, int &pos){
   
        int val = 0;
        while(pos < s.size() && isdigit(s[pos])) {
   
            val = val * 10 + s[pos] - '0';
            ++pos;
        }
        return val;
    }
    
    int get_depth(const string &s, int &pos){
   
        int depth = 0;
        while(pos < s.size() && s[pos] == '-') {
   
            ++depth;
            ++pos;
        }
        return depth;
    }
    
    
public:
    TreeNode* recoverFromPreorder(string s) {
   
        stack<pair<TreeNode*, int> >  st;
        TreeNode *root = nullptr;
        int pos = 0;
        root = new TreeNode(get_val(s, pos));
        st.push(make_pair(root, 0));
        
        while(pos < s.size()){
   
            int depth = get_depth(s, pos);
            int val = get_val(s, pos);
            TreeNode* cur = new TreeNode(val);
            
            
            TreeNode* top = st.top().first;
            int pre_depth = st.top().second;
            if(pre_depth + 1 == depth){
   
                top->left = cur;
            }else{
   
                while(pre_depth + 1 != depth){
   
                    st.pop();
                    top = st.top().first;
                    pre_depth = st.top().second;
                }   
                top->right = cur;
                st.pop();
            }
            st.push(make_pair(cur, depth));
        }
        
        return root;
    }
};
  1. Escape a Large Maze

题意:在一个 1 0 6 × 1 0 6 10^6\times10^6 106×106的棋盘上(索引从0到 1 0 6 − 1 10^6 - 1 1061),有一些障碍点(blocked最多200个),给定一个source和target两个点,问source能不能每次走上下左右四个方向到达target。

题解:如果不是棋盘这么大,直接BFS就能求解。稍微想一想就知道,这么少的障碍点,实际上大部分都是能通过的,我们可以将障碍点网里缩,而不改变source和target的连通性。具体做法就是将x坐标和y坐标重新布局索引。
直观的想象一下,如果不同的块之间有大于1的缝存在,我们只需要让这个缝等于1就可以了。
我们可以分别考虑x和y。考虑x的情况,先排序,如果有0,那么保持0,不然的话下标从1开始(把下方压缩为一行的空间)。
如果当前的x和前一个一样,那么保持相同的索引,如果比前面多一,那么保持多一。如果多二及以上,那么我们把它们的距离缩小到2,也就是索引加2。y同理。这样缩小得到的问题棋盘不会多于400 * 400。

题的数据有缺陷,标程也是:输入数据
[[99999,99998],[99998,99999]]
[0,0]
[99999,99999]
答案应该是false而标准程序给出的是true,并且数据中没有这样的例子。
这个和样例
[[0,1],[1,0]]
[0,0]
[1,1]
是一样的,只不过一个右上角,一个左下角。

class Solution {
   
public:
    
    int re_idx(vector<int> &xy_loc, map<int,int> &xy_map){
   
        int idx = 1;
        //边界触及0
        if(xy_loc[0] == 0) idx = 0;
        xy_map[xy_loc[0]] = idx;
        
        for(int i = 1;i < xy_loc.size(); ++i){
   
            if(xy_loc[i] == xy_loc[i - 1]) continue;
            if(xy_loc[i] == xy_loc[i - 1] + 1) xy_map[xy_loc[i]] = ++idx;
            else xy_map[xy_loc[i]] = (idx += 2);
        }
        //边界触及99999
        if(*xy_loc.rbegin() == 99999) return idx;
        return idx + 1;
    }
    
    bool isEscapePossible(vector<vector<int>>& blocked, vector<int>& source, vector<int>& target) {
   
        
        map<int,int> x_map, y_map;
        vector<int> x_loc, y_loc;
        x_loc.push_back(source[0]);
        x_loc.push_back(target[0]);
        y_loc.push_back(source[1]);
        y_loc.push_back(target[1]);
        
        
        for(int i = 0;i < blocked.size(); ++i){
   
            x_loc.push_back(blocked[i][0]);
            y_loc.push_back(blocked[i][1]);
        }
        
        int idx_x = 1, idx_y = 1;
        
        sort(x_loc.begin(), x_loc.end());
        sort(y_loc.begin(), y_loc.end());
        
        
        int x_max = re_idx(x_loc, x_map);
        int y_max = re_idx(y_loc, y_map);
        
        bool vis[x_max + 1][y_max + 1];
        memset(vis, 0, sizeof(vis));
        
        source[0] = x_map[source[0]];
        target[0] = x_map[target[0]];
        
        source[1] = y_map[source[1]];
        target[1] = y_map[target[1]];
        
        for(int i = 0;i < blocked.size(); ++i){
   
            int x = x_map[blocked[i][0]];
            int y = y_map[blocked[i][1]];
            vis[x][y] = true;
        }
        
        int x_dir[] = {
   1, 0, -1, 0};
        int y_dir[] = {
   0, 1, 0, -1};
        
        cout<<source[0]<<source[1]<<endl;
        cout<<target[0]<<target[1]<<endl;
        cout<<x_max<<y_max<<endl;
        
        queue<pair<int,int>> Q;
        Q.push(make_pair(source[0], source[1]));
        vis[source[0]][source[1]] = true;
        while(!Q.empty()){
   
            pair<int,int> u = Q.front(); Q.pop();
            int x = u.first, y = u.second;
            
            if(x == target[0] && y == target[1]) return true;
            
            for(int i = 0;i < 4; ++i){
   
                int next_x = x + x_dir[i];
                int next_y = y + y_dir[i];
                if(next_x < 0 || next_y < 0 || next_x > x_max || next_y > y_max || vis[next_x][next_y]) continue;
                vis[next_x][next_y] = true;
                Q.push(make_pair(next_x, next_y));
            }
        }
        return false;
        
    }
};
  1. Longest Duplicate Substring

题意;给定一个字符串,求里面最长的重复子串(出现2次或以上)。

题解:
解法1,二分答案+Rabin-Carp就是二分长度,然后滚动hash判断是否重复。
解法2,问题实际上就是求所有后缀的最长公共前缀,可以用后缀数组做法
下面是后缀数组代码

class Solution {
   
    static const int maxn = 1e5 + 2;

    int wa[maxn], wb[maxn], wv[maxn], ws[maxn], sa[maxn], height[maxn], *rank;
    int cmp(int *r, int a, int b, int l, int n){
   
        if(r[a] != r[b]) return false;
        if(a + l >= n && b + l >= n) return true;
        if(a + l >= n || b + l >= n) return false;
        return r[a + l] == r[b + l];
    }

    //待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n,且最大值小于m
    //sa是我们要求的后缀数组
    //这里x数组保存的值相当于是rank值。下面的操作只是用x数组来比较字符的大小,所以没有必要求出当前真实的rank值。所以这里x保存位置i的字符就好
    //后面的x才是rank值,x[i]=后缀i的名次
    int* cal_suffix_arr(int *r, int *sa, int n, int m){
   
        int *x = wa,*y = wb;
        memset(ws, 0, m * sizeof(int));
        //对第一个字符排序
        for(int i = 0;i < n; ++i) ++ws[x[i] = r[i]];
        for(int i = 1;i < m; ++i) ws[i] += ws[i - 1];
        for(int i = n - 1; i >=0; --i) sa[--ws[x[i]]] = i;
    
        
        //对长度为2^j的后缀排序
        for(int j = 1, p = 0; p < n; j*=2, m = p){
   
            p = 0;
            //接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,
            //第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次。
            //数组y保存的是对第二关键字排序的结果,y[p]= 排名为p的为哪个后缀的第二关键字
            //因为从n - j开始后面的第二部分都是空串,所以排在前面
            for(int i = n - j; i < n; ++i) y[p++] = i;
            for(int i = 0; i < n; ++i) if(sa[i] >= j) y[p++] = sa[i] - j;
            //wv[i] = x[y[i]]是第二关键字排名为i的后缀的第一关键字排名
            //基数排序。按第二关键字排序后,第一关键字的原排名。用第一关键字分桶就得到了一二关键字的排序
            //ws是m个桶
            for(int i = 0; i < n; ++i) wv[i] = x[y[i]];
            for(int i = 0; i < m; ++i) ws[i] = 0;
            //分桶,排序
            for(int i = 0; i < n; ++i) ++ws[wv[i]]; //++ws[x[i]]也一样
            for(int i = 1; i < m; ++i) ws[i] += ws[i - 1];
            for(int i = n - 1; i >= 0; --i) sa[--ws[wv[i]]] = y[i];
            //求解新的rank数组x
            //根据sa求rank。sa[i]是排名为i的后缀,rank[sa[i]]后缀sa[i]的排名
            //因为这里的sa数组对于后缀相同时,排名按位置前后排,所以这里rank还需要判重
            //对于两个后缀sa[i - 1]和sa[i],他们第一关键字和第二关键字是否都一样,这个可以通过判断本来的rank在对应位置是否相同
            swap(x, y); x[sa[0]]=0; p = 1;
            for(int i = 1; i < n; ++i){
   
                x[sa[i]] = cmp(y, sa[i - 1], sa[i], j, n) ? p - 1: p++;
                
            }
        }
        return x;
    }


    void cal_height(int n, int *sa, int *rank, int *r, int *height) {
   
        // vector<int> rank(n);
        // for(int i = 0; i < n; ++i)rank[sa[i]] = i;

        int k = 0;
        for(int i = 0; i < n; ++i){
   
            int cur = rank[i];
            if(cur == 0) {
   
                height[cur] = 0;
                k = 0;
            } else {
   
                if(k > 0) --k;
                int j = sa[cur-1];
                while(i + k < n && j + k < n && r[i + k] == r[j + k]) ++k;
                height[cur] = k;
            }
        }
    }
    
public:
    string longestDupSubstring(string S) {
   
        int n = S.size(), m = 26;
        int *s = new int[n];
        for(int i = 0;i < n; ++i){
   
            s[i] = S[i] - 'a';
        }
        
        rank = cal_suffix_arr(s, sa, n, m);
        cal_height(n, sa, rank, s, height);
        int max_len = 0, pos = 0;
        
        
        for(int i = 1;i < n; ++i){
   
            if(height[i] > max_len){
   
                max_len = height[i];
                pos = sa[i];
            }
        }
        delete []s;
        return S.substr(pos, max_len);
        
        return "";
    }
};
  1. Number of Submatrices That Sum to Target

题意:给定一个至多300*300的整数矩阵,和一个target,问有多少子矩阵的和为target

题解:做过最大矩阵和的应该很容易想到,这个用前缀和以及HashMap可以使得复杂度为O(N^3)。
具体的就是先求矩阵前缀和。然后枚举上下边,变成一维问题。
也就是给定一个数组,求和为target的子段有多少?
有了前缀和,枚举每个位置i,前缀和为pre_sum[i],就是找前面有多少个j使得pre_sum[i] - pre_sum[j] = target
转化一下,就是求i前面的前缀和有多少个等于pre_sum[i] - target。这个可以用一个map来保存计数。

class Solution {
   
public:
    int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {
   
        int n = matrix.size(), m = matrix[0].size();
        
        vector<vector<int>> pre_sum(n + 1, vector<int>(m + 1, 0));
        
        
        for(int i = 1;i <= n; ++i)
            for(int j = 1; j <= m; ++j)
                pre_sum[i][j] = pre_sum[i - 1][j] + pre_sum[i][j - 1] + matrix[i - 1][j - 1] - pre_sum[i - 1][j - 1];
         unordered_map<int,int> pre_sum_catch;
        
        int ans = 0;
        for(int i = 1; i <= n; ++i)
            for(int j = i; j <= n; ++j){
   
                pre_sum_catch.clear();
                pre_sum_catch[0] = 1;
                for(int k = 1; k <= m; ++k){
   
                    int p = pre_sum[j][k] - pre_sum[i - 1][k];
                    ans += pre_sum_catch[p - target];
                    ++pre_sum_catch[p];
                }
            }
        return ans;
    }
};
  1. Shortest Common Supersequence

题意:给定两个字符串,求一个最短的字符串能将这两个串作为子序列

题解:动态规划。dp[i][j]表示将str1的前缀i和str2的前缀j作为子序列的串的最短长度。
则当前串的最短长度的串,考虑最后一个字符,首先它必须等于str1[i]或者str2[j]
如果str1[i] == str2[j] 那么最后一个字符和他们都相等,则同时考虑它们两个不会产生更差的结果(就是贪心),转化为子问题,dp[i][j] = dp[i - 1][j - 1] + 1
如果不相等,那么考虑最后一个字符的两种情况取最优。

class Solution {
   
public:
    string shortestCommonSupersequence(string str1, string str2) {
   
        int n = str1.length(), m = str2.length();
        int dp[n + 1][m + 1];
        
        //init
        for(int i = 0;i <= n; ++i) dp[i][0] = i;
        for(int j = 0;j <= m; ++j) dp[0][j] = j;
        
        
        for(int i = 1;i <= n; ++i){
   
            for(int j = 1;j <= m; ++j){
   
                if(str1[i - 1] == str2[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
            }
        }
        
        vector<char> s;
        int cur_i = n, cur_j = m;

        //回溯答案        
        for(int k = 0; k < dp[n][m]; ++k){
   
            if(cur_i > 0 && cur_j > 0 && str1[cur_i - 1] == str2[cur_j - 1]){
   
                s.push_back(str1[cur_i - 1]);
                --cur_i; --cur_j;
            }else{
   
                if(cur_i > 0 && dp[cur_i - 1][cur_j] + 1 == dp[cur_i][cur_j]){
   
                    s.push_back(str1[cur_i - 1]);
                    --cur_i;
                }else{
   
                    s.push_back(str2[cur_j - 1]);
                    --cur_j;
                }
            }
        }
         
        return string(s.rbegin(), s.rend());
        
    }
};
  1. Find in Mountain Array

题意:交互题。一个数组称为MountainArray,如果它的长度至少位3,且存在一个0<i<len - 1使得0到i递增,i到len - 1递减

题解:不能直接操作数组,要通过一个类获取第k个值。先找出最大值所在的下标。通过二分搜索得到,然后先搜索前半段,再搜索后半段即可。

/**
 * // This is the MountainArray's API interface.
 * // You should not implement it, or speculate about its implementation
 * class MountainArray {
 *   public:
 *     int get(int index);
 *     int length();
 * };
 */
class Solution {
   
public:
    int findInMountainArray(int target, MountainArray &mountainArr) {
   
        int n = mountainArr.length();
        
        int left = 0, right = n - 1, peak_idx;
        
        while(left <= right){
   
            int mid = (left + right) / 2;
            int mid_num = mountainArr.get(mid);
            
            if(mountainArr.get(mid + 1) < mid_num){
   
                right = mid - 1;
            }else
                left = mid + 1;
        }
        
        peak_idx = right + 1;
        left = 0; right = peak_idx;

        while(left <= right){
   
            int mid = (left + right) / 2;

            int mid_num = mountainArr.get(mid);
            if(mid_num <= target){
   
                left = mid + 1;
            }else
                right = mid - 1;
        }
        
        if(left > 0 && mountainArr.get(--left) == target) return left;
        
        left = peak_idx + 1; right = n - 1;
        
        while(left <= right){
   
            int mid = (left + right) / 2;
            int mid_num = mountainArr.get(mid);
            if(mid_num >= target){
   
                left = mid + 1;
            }else
                right = mid - 1;
        }
        
        if(mountainArr.get(--left) == target) return left;
        return -1;
    }
};
  1. Brace Expansion II

题意:给定一个表达式,形式如 “{ {a,z},a{b,c},{ab,z}}”,其中大括号表示可选内容。{a,z}表示a或者z,a{b,c}表示ab或者ac。所以前面的串能表示的有[“a”,“ab”,“ac”,“z”]

给定一个大括号表达式, 问这个串能表示哪些串,按字典序返回。

题解:采用递归,分解成子问题解决。比较难搞的一个模拟题。写的比较挫。
首先如果整个串被一个大括号包含,那么答案就是每个逗号分隔部分答案的并
如果不被括号包围,那么就是各部分答案的笛卡尔积

class Solution {
   
    //判断是否被一个大括号包围整个串
    bool check_split(string expression){
   
        int brace_num = 0;
        if(*expression.rbegin() != '}' or *expression.begin() != '{') return false;
        
        for(int i = 0;i < expression.length() - 1; ++i){
   
            if(expression[i] == '{') ++brace_num;
            else if(expression[i] == '}') --brace_num;

            if(brace_num == 0) return false;
        }
        return true;
    }
    
    //将串按照逗号分隔为子串
    vector<string> split(string expression){
   
        vector<string> vs;
        
        for(int i = 1; i < expression.size() - 1; ++i){
   
            int j = i + 1;
            int brace_num = (expression[i] == '{');
            
            while(j < expression.size() - 1 and (expression[j] != ',' or brace_num != 0)){
   
                switch(expression[j++]){
   
                    case '}': --brace_num; break;
                    case '{': ++brace_num; break;
                }
            }
            vs.push_back(expression.substr(i, j - i));
            
            i = j;
        }
        return vs;
    }
    //计算笛卡尔积
    vector<string> merge_strs(vector<vector<string>> &vs){
   
        vector<string> v(1,"");
        
        for(int i = 0;i < vs.size(); ++i){
   
            vector<string> t;
            for(int j = 0;j < v.size(); ++j){
   
                for(int k = 0;k < vs[i].size(); ++k){
   
                    t.push_back(v[j] + vs[i][k]);
                }
            }
            v.swap(t);
        }
        sort_and_remove_duplicate(v);
        return v;
    }
    //排序,去重
    void sort_and_remove_duplicate(vector<string> &v){
   
        sort(v.begin(), v.end());
        int pos = 0;
        for(int i = 0;i < v.size(); ++i){
   
            if(i < v.size() - 1 and v[i] == v[i + 1] ) continue;
            v[pos++] = v[i];
        }
        v.resize(pos);
    }
    
    
public:
    vector<string> braceExpansionII(string expression) {
   
        
        vector<string> vs;
        string substr = "";
        int brace_count = 0;
       //分两种情况
        if(check_split(expression)){
   
            vector<string> strs = split(expression);
            
            for(int i = 0;i < strs.size(); ++i){
   
                vector<string> t = braceExpansionII(strs[i]);
                vs.insert(vs.end(), t.begin(), t.end());
            }
            
            sort_and_remove_duplicate(vs);
            return vs;
        }
        
        vector<vector<string> >substrs;
        for(int i = 0;i < expression.size(); ++i){
   
            int j = i + 1;
            if(expression[i] == '{'){
   
                int brace_num = 0;
                
                while(expression[j] != '}' or brace_num != 0){
   
                    switch(expression[j++]){
   
                        case '}': --brace_num; break;
                        case '{': ++brace_num; break;
                    }
                }
                substrs.push_back(braceExpansionII(expression.substr(i, j - i + 1)));
                
            //字母
            }else{
   
                j = i;
                while(j + 1 < expression.size() and expression[j + 1] != '{') ++j;
                substrs.push_back(vector<string>(1, expression.substr(i, j - i + 1)));

            }
            i = j;
        }
        
        return merge_strs(substrs);
        
    }
};
  1. Parsing A Boolean Expression

题意:给定一个真值表达式,包函数字符{’(’, ‘)’, ‘&’, ‘|’, ‘!’, ‘t’, ‘f’, ‘,’},求表达式的真值。
例如
Input: expression = “|(&(t,f,t),!(t))”
Output: false
题解:采用栈的方式进行计算,遇到有括号时,计算栈顶部分的表达式,所有表达式都计算完。因为没有空格,且都是合法的表达式,所以比较简单。

class Solution {
   
    
    
    bool is_opt(char e){
   
        return e == '&' || e == '|' || e == '!' || e == ' ';
    }
    
public:
    bool parseBoolExpr(string expression) {
   
        stack<char> s;
        stack<bool> val;
        char opt;
        bool cur_val;
        
        for(int i = 0;i < expression.size(); ++i){
   
            //碰到运算符,因为不存在空格,所以直接可以continue了
            //而且表达式都有括号,所以在遇到左括号时push,不然的话在遇到运算符就可push了
            if(is_opt(expression[i]) || expression[i] == ',') continue;
            //非运算符
            
            //左括号
            if(expression[i] == '('){
   
                if(i > 0 && is_opt(expression[i - 1])) s.push(expression[i - 1]);
                else s.push(' ');
            //右括号
            }else if(expression[i] == ')'){
   
                while(!is_opt(s.top())){
   
                    val.push(s.top() == 't');
                    s.pop();
                }
                opt = s.top(); s.pop();
                cur_val = (opt == '&');
                    
                while(!val.empty()){
   
                    switch(opt){
   
                        case ' ' : cur_val = val.top(); break;
                        case '|': cur_val |= val.top(); break;
                        case '&': cur_val &= val.top(); break;
                        case '!': cur_val = !val.top(); break;
                    }
                    val.pop();
                }
                if(cur_val) s.push('t');
                else s.push('f');
            // expression[i] == 't'或者'f'
            }else{
   
                s.push(expression[i]);
            }   
        }   
        return s.top() == 't';
        
    }
};
  1. Longest Chunked Palindrome Decomposition

题意:给定一个字符串text。问最多能把它分成多少个子串,text=(a_1, a_2,…,a_k)使得a_i = a_{k + 1 - i}

题解:贪心。贪心做分割。假设前缀a能和后缀a匹配,如果存在一个前缀包含a,假设为a b,能和后缀匹配。那么在a这里分割比在b处分割更好。
证明: 设前缀[a, c, b] 和后缀[a, a, c] 或者[a,c,a]匹配(分别对应的情况是[a c] 和末尾的[a c]或者[a]和末尾的[a]匹配,其中len(a) == len(b)),(中括号表示拼接,c可以为空串)。

  • 情形1: [ a , c , b ] [a,c,b] [a,c,b] [ a , a , c ] [a,a,c] [a,a,c]匹配。则有 [ c , b ] =
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值