• (持续更新) 十一月每日一题打卡


    1 每日一题打卡

    11.1 检查两个字符串数组是否相等 lc 1662

    class Solution {
    public:
        bool arrayStringsAreEqual(vector& word1, vector& word2) {
            int n1 = word1.size(), n2 = word2.size();
            int i1 = 0, i2 = 0;
            int j1 = 0, j2 = 0;
    
            while(i1 < n1 && i2 < n2)
            {
                if(j1 < word1[i1].size() && j2 < word2[i2].size())
                {
                    if(word1[i1][j1] != word2[i2][j2])
                        return false;
                    cout << j1 << " " << j2 << endl;
                    j1++; j2++;
                }
                else
                {
                    if(j1 >= word1[i1].size())
                    {
                        j1 = 0; i1++;
                    }
                    if(j2 >= word2[i2].size())
                    {
                        j2 = 0; i2++;
                    }
                }
            }
            cout << i1 << " " << i2 << endl;
            if(i1 >= n1 && i2 >= n2)
                return true;
            return false;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    11.3 最大重复字符串 lc 1668

    序列dp

    class Solution {
    public:
        int maxRepeating(string sequence, string word) {
            int sn = sequence.size(), wn = word.size();
            if(sn < wn) return 0;
            int dp[sn + 1];
            int ans = 0;
            memset(dp, 0, sizeof dp);
            for(int i = 1; i <= sn; i++)
            {
                if(i < wn) continue;
                string str = sequence.substr(i - wn, wn);
                if(str == word)
                    dp[i] = dp[i - wn] + 1;
                ans = max(ans, dp[i]);
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    上述算法在匹配str==word时使用的是暴力一个一个匹配可以转换成kmp匹配字符串。在kmp匹配过程中,匹配上就在之前匹配上+1,取最大值。

    字符串匹配算法:KMP 字符串p在字符串s中匹配

    //kmp
    #include 
    using namespace std;
    
    const int N = 100010;
    int ne[N];
    int n, m;
    string p, s;
    
    int main(){
        cin >> n >> p >> m >> s;
        //i是待匹配位置,j是已匹配长度,j - 1是y匹配的最后一个位置
        //与自身匹配i从1开始
        for(int i = 1, j = 0; i < n; i ++){
            while(j && p[i] != p[j]) j = ne[j - 1];
            if(p[i] == p[j]) j ++;
            ne[i] = j;
        }
    
    //与长字符串匹配i从0开始
        for(int i = 0, j = 0; i < m; i ++){
            while(j && s[i] != p[j]) j = ne[j - 1];
            if(s[i] == p[j]){
                j ++;
                if(j == n){
                    cout << i - n + 1 << " ";
                    j = ne[n - 1];
                }
            }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    const int N = 110;
    int ne[N];
    class Solution {
    public:
        void getNext(string word)
        {
            memset(ne, 0, sizeof ne);
            int wn = word.size();
            int j = 0;
            for(int i = 1; i < wn; i++)
            {
                while(j && word[i] != word[j]) j = ne[j - 1];
                if(word[i] == word[j]) j++;
                ne[i] = j;
            }
        }
        int maxRepeating(string sequence, string word) {
            int sn = sequence.size(), wn = word.size();
            if(sn < wn) return 0;
            getNext(word);
            int dp[sn];
            int ans = 0;
            memset(dp, 0, sizeof dp);
            int j = 0;
            for(int i = 0; i < sn; i++)
            {
                while(j && sequence[i] != word[j]) j = ne[j - 1];
                if(sequence[i] == word[j]) j++;
                if(j == wn)
                {
                    if(i < wn) dp[i] = 1;
                    else dp[i] = dp[i - wn] + 1;
                    ans = max(ans, dp[i]); 
                    j = ne[j - 1];  //接着匹配
                }
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    11.4 到达终点的数字 lc 754

    class Solution {
    public:
        int reachNumber(int target) {
            target = abs(target);
            int s = 0, n = 0;
            while(s < target || (s - target) % 2)
                s += ++n;
            return n;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    一道很奇妙的数学题

    754-2.png

    11.5 解析布尔表达式 lc 1106

    一道括号栈模拟,想清楚每一步即可

    class Solution {
    public:
        stack op;
        stack num;
        //(是 -1
        bool calu(char opp)
        {
            bool ans;
            bool ans1 = true;
            bool ans2 = false;
            while(num.size() && num.top() != -1)
            {
                bool t = num.top();
                num.pop();
                if(opp == '!')
                    ans = !t;
                else if(opp == '&')
                {
                    if(!t) ans1 = false;
                }
                else if(opp == '|')
                {
                    if(t) ans2 = true;
                }
            }
            num.pop();
            if(opp == '!') return ans;
            else if(opp == '&') return ans1;
            else return ans2;
        }
        bool tran(char x)
        {
            return x == 'f'? false: true;
        }
        bool parseBoolExpr(string exp) {
            int n = exp.size();
            int i = 0;
            while(i < n){
                if(exp[i] == '!' || exp[i] == '&' || exp[i] == '|')
                    op.push(exp[i++]);
                else if(exp[i] == '('){
                    num.push(-1);
                    i++;
                }    
                else if(exp[i] == ')'){
                    char opp = op.top();
                    op.pop();
                    bool r = calu(opp);
                    num.push(r);
                    i++;
                }
                else if(exp[i] == 'f' || exp[i] == 't'){
                    num.push(tran(exp[i++]));
                    if(i < n && exp[i] == ',') i++;
                }
                else  i++;
            } 
            return num.top();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    11.7 模糊字符串 lc 816

    很烦的一道题,在于我没有理清逻辑,一步一步的写

    分为加逗号和加小数点,加完逗号去加小数点。判断小数点是否合理:整数部分如果大于1个数的话开头不能是0,小数部分最后不能是0。

    class Solution {
    public:
        vector ambiguousCoordinates(string s) {
            s = s.substr(1, s.size() - 2);
            int n = s.size();
            vector ans;
            for(int i = 1; i < n; i++)
            {
                vector xp = addPoint(s.substr(0, i));
                vector yp = addPoint(s.substr(i));
                for(int j = 0; j < xp.size(); j++)
                {
                    for(int k = 0; k < yp.size(); k++)
                    {
                        ans.push_back("(" + xp[j] + ", " + yp[k] + ")");
                    }
                }
            }
            return ans;
        }
        vector addPoint(string s)
        {
            if(s.size() == 1) return {s};
            vector ans;
            if(s.size() > 0 && s[0] != '0') ans.push_back(s);
            for(int i = 1; i < s.size(); i++)
            {
                string a = s.substr(0, i);
                string b = s.substr(i);
                if(a.size() > 1 && a[0] == '0') continue;
                if(b.size() > 0 && b[b.size() - 1] == '0') continue;
                ans.push_back(a + "." + b);
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    11.9 最大加法符号 lc 764

    简单dfs可以过,注意mines数组不是图里的二维矩阵,需要先转换一下。复杂度 O ( n 2 ) O(n^2) O(n2)的指数级别。

    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    class Solution {
    public:
        int ans = 0;
        int n;
        
        void dfs(vector>& grid, int x, int y, int& k)
        {
            for(int i = 0; i < 4; i++)
            {
                int nx = x + k * dx[i];
                int ny = y + k * dy[i];
                if(nx < 0 || nx >= n || ny < 0 || ny >= n)
                    return;
                if(grid[nx][ny] == 0)
                    return;
            }
            k++;
            dfs(grid, x, y, k);
        }
        int orderOfLargestPlusSign(int nn, vector>& mines) {
            n = nn;
            vector> grid(n, vector(n, 1));
            for(int i = 0; i < mines.size(); i++)
                grid[mines[i][0]][mines[i][1]] = 0;
            for(int i = 0; i < n; i++)
            {
                for(int j = 0; j < n; j++)
                {
                    if(grid[i][j] == 1)
                    {
                        int k = 1;
                        dfs(grid, i, j, k); 
                        ans = max(ans, k);
                    }
                }
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    动态规划:也不算dp啦,四个方向上的前缀和。搞四个数组,up[i] [j]表示(i, j)向上能最多扩张几个1, down[i] [j], left[i] [j], right[i] [j]同理。注意down和right要倒着遍历。复杂度 O ( n 2 ) O(n^2) O(n2)

    class Solution {
    public:
        int orderOfLargestPlusSign(int n, vector>& mines) {
            int ans = 0;
            vector> grid(n, vector(n, 1));
            for(int i = 0; i < mines.size(); i++)
                grid[mines[i][0]][mines[i][1]] = 0;
            int up[n + 2][n + 2], down[n + 2][n + 2], left[n + 2][n + 2], right[n + 2][n + 2];
            memset(up, 0, sizeof up);
            memset(down, 0, sizeof down);
            memset(left, 0, sizeof left);
            memset(right, 0, sizeof right);
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= n; j++)
                {
                    if(grid[i - 1][j - 1] == 1)
                    {
                        up[i][j] = up[i - 1][j] + 1;
                        left[i][j] = left[i][j - 1] + 1;
                    }
                    ans = max(ans, min(min(up[i][j], down[i][j]), min(left[i][j], right[i][j])));
                }
            }
            for(int i = n; i >= 1; i--)
            {
                for(int j = n; j >= 1; j--)
                {
                    if(grid[i - 1][j - 1] == 1)
                    {
                        down[i][j] = down[i + 1][j] + 1;
                        right[i][j] = right[i][j + 1] + 1;
                    }
                    ans = max(ans, min(min(up[i][j], down[i][j]), min(left[i][j], right[i][j])));
                }
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    11.9 扁平化多级双向链表 lc 430

    dfs是获得以head为头节点的已经整理好的链表,分为三步:保存next节点,将整理好的dfs(child)节点作为next,整理好prev和child指针指向。然后找到左侧最后面那个节点,将整理好的dfs(tmp)作为最后那个节点的next,整理好prev和child指针指向。

    /*
    // Definition for a Node.
    class Node {
    public:
        int val;
        Node* prev;
        Node* next;
        Node* child;
    };
    */
    
    class Solution {
    public:
        Node* dfs(Node* head){
            if(head == nullptr)
                return nullptr;
            
            Node* tmp = head->next;
            
            Node* childNext = dfs(head->child);
            if(childNext == nullptr)
            {
                head->next = dfs(tmp);
                return head;
            }
            head->next = childNext;
            childNext->prev = head;
            head->child = nullptr;
            
            Node* p = head;
            while(p->next != nullptr)
                p = p->next;
            Node* nodeNext = dfs(tmp);
            if(nodeNext == nullptr)
                return head;
            p->next = nodeNext;
            nodeNext->prev = p;
            return head;
            
        }
        Node* flatten(Node* head) {
            if(head == nullptr) return nullptr;
            return dfs(head);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    11.10 获得所有钥匙的最短路径 lc 864

    图中的最短路径:BFS很好用

    这道题的关键在于visited数组不是取决于这个坐标是否访问过,如果获得了新的钥匙可以重新返回原来的路径,也就是还需要加入钥匙的状态,需要状态压缩。

    • 初始化全部钥匙都获得的状态allKeyState = (1 << keyN) - 1 1向左左移keyN然后-1使得全为1
    • 获取第ch位是否为1:(k >> ch) & 1 k向右移ch位并获取
    • 使第ch位为1:k | (1 << ch)
    class Solution { 
    public:
        struct State{
            int x, y, key;
        };
        int shortestPathAllKeys(vector& grid) {
            int m = grid.size(), n = grid[0].size();
            int keyN = 0, cnt = 0, ans = 0;
            int dx[4] = {-1, 1, 0, 0};
            int dy[4] = {0, 0, -1, 1};
            State s;
            queue q;
            for(int i = 0; i < m; i++)
            {
                for(int j = 0; j < n; j++)
                {
                    if(grid[i][j] == '@')
                    {s.x = i; s.y = j; s.key = 0;}
                    if(islower(grid[i][j])) keyN++;
                }
            }
            q.push(s);
            int allKeyState = (1 << keyN) - 1;
            vector>> visited(m, vector>(n, vector(allKeyState + 1, false)));
            while(q.size())
            {
                int qn = q.size();
                for(int i = 0; i < qn; i++)
                {
                    State tmp = q.front();
                    q.pop();
                    if(tmp.key == allKeyState)
                        return ans;
                    for(int j = 0; j < 4; j++)
                    {
                        int nx = tmp.x + dx[j], ny = tmp.y + dy[j], k = tmp.key;
                        if(nx < 0 || nx >= m || ny < 0 || ny >= n)
                            continue;
                        char ch = grid[nx][ny];
                        if(ch == '#')
                            continue;
                        if(isupper(ch) && (k >> (ch - 'A') & 1) == 0)
                            continue;
                        if(islower(ch))
                            k |= 1 << (ch - 'a');
                        if(visited[nx][ny][k])
                            continue;
                        visited[nx][ny][k] = true;
                        q.push(State{nx, ny, k});
                    }
                }
                ans++;
            }
            return -1;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    11.13 自定义字符串排序

    还行比较简单

    class Solution {
    public:
        string customSortString(string order, string s) {
            int cnt[26];
            memset(cnt, 0, sizeof cnt);
            for(int i = 0; i < s.size(); i++)
                cnt[s[i] - 'a']++;
            string ans;
            for(int i = 0; i < order.size(); i++)
            {
                while(cnt[order[i] - 'a']--)
                    ans += order[i];
            }
            for(int i = 0; i < 26; i++)
            {
                while(cnt[i] > 0)
                {
                    ans +=  (i + 'a');
                    cnt[i]--;
                }
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    11.14 数组的均值分割 lc 805

    通过计算我们可以得到avg(A)=avg(B) = avg(nums),sum(A) = sum(nums) * k / n,为了防止浮点数,我们可以将所有数*n再减去avg(nums),这样就转换成在n个数中找k个数使他们的和为0即可。也就是n个数每个数的选与不选问题,复杂度是 2 n 2^n 2n,n取到30太大了。考虑折半

    也就是说分为A0和A1两个数组,各一半。从A0中找tot,A1中找-tot,加一块等于0也行。或者说在A0/A1中就找到了和为0的也行。但是有一种情况不行,就是全选左边(或者右边,全选左边就代表也必须全选右边因为lsum=-rsum,这样A数组就全选了所有nums中的,B就没有了)

    二进制枚举问题

    n个数中选取k个的方案问题,一共有[0, 2 n − 1 2^n - 1 2n1]这些方案。

    遍历这些方案for(int i = 0; i < (1 << n); i++)

    获取每个方案中是否选了第j个 if(i & (1 << j))

    class Solution {
    public:
        bool splitArraySameAverage(vector& nums) {
            int n = nums.size();
            if(n == 1) return false;
            int m = n / 2;
            int sum = 0;
            for(int x: nums) sum += x;
            for(int& x: nums)
                x = x * n - sum;
            cout << m << endl;
            unordered_set leftsum;
            for(int i = 1; i < (1 << m); i++) //方案二进制们
            {
                //不能从0开始,也就是A0中一个都不选,可以这样做,不适合这个循环,可以之间跳过。
                int tot = 0;
                for(int j = 0; j < m; j++) //第j个元素
                {
                    if(i & (1 << j))
                        tot += nums[j];
                }
                if(tot == 0)
                    return true;
                leftsum.insert(tot);
            }
            int rsum = 0;
            for(int i = m; i < n; i++) rsum += nums[i];
            cout << rsum << endl;
            for(int i = 1; i < 1 << (n - m); i++)
            {
                int tot = 0;
                for(int j = m; j < n; j++)
                {
                    if(i & (1 << (j - m)))
                        tot += nums[j];
                }
                if(tot == 0 || (rsum != tot && leftsum.find(-tot) != leftsum.end()))
                    return true;
            }
            return false;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    2 DP专题

    在这里插入图片描述

    11.1 跳跃游戏

    跳跃游戏I lc 55

    dp[i]表示当前位置能否跳到,一个一个跳着向前找。

    适当剪枝:如果跳几个就已经可以了,就不必向前找了。

    class Solution {
    public:
        bool canJump(vector& nums) {
            int n = nums.size();
            bool dp[n];
            memset(dp, false, sizeof dp);
            dp[0] = true; 
            for(int i = 1; i < n; i++)
            {
                for(int j = 1; j <= i; j++)
                {
                    if(j <= nums[i - j])
                    {
                        dp[i] = dp[i] || dp[i - j];
                        if(dp[i])
                            break;
                    }
                }
            }
            return dp[n - 1];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    class Solution {
    public:
        bool canJump(vector& nums) {
            int k = 0; //当前能跳的最远距离
            for(int i = 0; i < nums.size(); i++)
            {
                if(i > k)
                    return false;
                k = max(k, i + nums[i]);
                if(k >= nums.size() - 1)
                    return true;
            }
            return true;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    k表示当前能跳的最远距离,如果i大于最远距离当然不行,如果k大于整个数组长度,那就可以啦!

    原理就是如果这个位置能跳到,那么左侧所有位置都能跳到。因为nums[i]存储的是最大距离,0, 1, 2, …到nums[i]都可以。

    不用拘泥于每次究竟跳跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。

    跳跃游戏II lc 45

    在前面dp的基础上改进即可,找到前面能够跳跃的位置+1,取每个的最小值。5%太慢了, O ( n 2 ) O(n^2) O(n2)

    class Solution {
    public:
        int jump(vector& nums) {
            int n = nums.size();
            int dp[n];
            memset(dp, 0x3f, sizeof dp);
            dp[0] = 0; 
            for(int i = 1; i < n; i++)
            {
                for(int j = 1; j <= i; j++)
                {
                    if(j <= nums[i - j])
                        dp[i] = min(dp[i], dp[i - j] + 1);
                }
            }
            return dp[n - 1];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    贪心解法:

    11.2 打家劫舍系列

    打家劫舍 I lc 198

    class Solution {
    public:
        int rob(vector& nums) {
            int n = nums.size();
            int dp[n + 1];
            dp[0] = 0; dp[1] = nums[0];
            for(int i = 2; i <= n; i++)
            {
                dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
            }
            return dp[n];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    打家劫舍 II lc 213

    分两种情况:

    • 第一个房子偷:那只能偷到第n- 1个
    • 第一个房子不偷:那可以偷到第n个
    class Solution {
    public:
        int rob(vector& nums) {
            int n = nums.size();
            if(n == 1) return nums[0];
            int dp[n + 1];
            dp[0] = 0; dp[1] = 0;
            
            for(int i = 2; i <= n; i++)
                dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
            dp[0] = 0; dp[1] = nums[0];
            for(int i = 2; i <= n - 1; i++)
                dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
            return max(dp[n - 1], dp[n]);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    11.3 解码方式系列

    解码方式 I lc 91

    前i位的解码方案个数取决于和第i位组成的是两位还是一位。

    class Solution {
    public:
        int numDecodings(string s) {
            int n = s.size();
            int dp[n + 1];
            if(s[0] == '0') return 0;
            memset(dp, 0, sizeof dp);
            dp[0] = 1; 
            dp[1] = 1;
            for(int i = 2; i <= n; i++)
            {
                if(s[i - 1] != '0')
                    dp[i] += dp[i - 1];
                if(s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))
                    dp[i] += dp[i - 2];
            }
            return dp[n];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    解码方式 II lc 639

    添加了一个通配符,把每个if else搞清楚就行

    class Solution {
    public:
        const int mod = 1e9 + 7;
        int numDecodings(string s) {
            int n = s.size();
            long long dp[n + 1];
            memset(dp, 0, sizeof dp);
            dp[0] = 1;
            if(s[0] == '0') return 0;
            else if(s[0] == '*') dp[1] = 9;
            else dp[1] = 1;
            for(int i = 2; i <= n; i++)
            {
                if(s[i - 1] != '0' && s[i - 1] != '*')
                    dp[i] += dp[i - 1] % mod;
                else if(s[i - 1] == '*')
                    dp[i] += dp[i - 1] * 9 % mod;
                //if(dp[i] < 0)
                    //dp[i] += mod;
                dp[i] = dp[i] % mod;
                if(s[i - 1] != '*' && (s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6' && s[i - 1] >= '0')))
                    dp[i] += dp[i - 2] % mod;
                else if(s[i - 2] == '*' && s[i - 1] != '*')
                {
                    if(s[i - 1] <= '6' && s[i - 1] >= '0')
                        dp[i] += dp[i - 2] * 2 % mod;
                    else
                        dp[i] += dp[i - 2] % mod;
                }
                else if(s[i - 2] != '*' && s[i - 1] == '*')
                {
                    if(s[i - 2] == '1')
                        dp[i] += dp[i - 2] * 9 % mod;
                    else if(s[i - 2] == '2')
                        dp[i] += dp[i - 2] * 6 % mod;
                }
                else if(s[i - 2] == '*' && s[i - 1] == '*')
                    dp[i] += dp[i - 2] * 15 % mod;
                dp[i] = dp[i] % mod;
            }
            return dp[n];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    11.4 石子问题(区间dp)

    石子游戏 lc 877

    不太理解这个差值

    f[l] [r]表示先手与后手的最大差值得分

    先手取左边f[l] [r] = p[l - 1] - f[l + 1] [r]

    先手取右边 f[l] [r] = p[r - 1] + f[l] [r]

    大区间依赖于小区间:区间dp的常规操作就是枚举区间长度和左区间

    class Solution {
    public:
        bool stoneGame(vector& piles) {
            int n = piles.size();
            int f[n + 2][n + 2];
            memset(f, 0, sizeof f);
            for(int len = 1; len <= n; len++)
            {
                for(int l = 1; l + len - 1 <= n; l++)
                {
                    int r = l + len - 1;
                    f[l][r] = max(piles[l - 1] - f[l + 1][r], piles[r - 1] - f[l][r - 1]);
                }
            }
            return f[1][n] > 0;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    石子合并 acwing

    每次都是合并相邻两堆:这样最后合并的两堆一定是左边区间和右边区间

    f[l] [r]表示合并从[l, r]区间的最小体力

    集合:所有将[i, j]区间合并的方案

    f[l] [r] 从[l, r]中选一个k,分治到[l, k] [k + 1] [r]

    f [ l ] [ r ] = f [ l ] [ k ] + f [ k + 1 ] [ r ] + s u m ( i , j ) f[l][r] = f[l][k] + f[k + 1][r] + sum(i, j) f[l][r]=f[l][k]+f[k+1][r]+sum(i,j)加上合并最后两堆的体力值,就是里面所有数目的和

    int n = stones.size();
        int dp[n + 1][n + 1];
        memset(dp, 0x3f, sizeof dp);
    
        for(int len = 2; len <= n; len++)
        {
            for(int l = 1; i + len - 1 <= n; i++)
            {
                int r = i + len - 1;
                for(int k = l + 1; k <= j - 1; k++)
                    f[i][j] = min(f[i][j], f[l][k] + f[k + 1][r] + sum(i, j));
            }
        }
        return f[1][n];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    11.5 单词划分系列

    单词划分I lc 139

    完全背包问题

    class Solution {
    public:
        bool wordBreak(string s, vector& wordDict) {
            int n = s.size();
            bool dp[n + 1];
            memset(dp, false, sizeof dp);
            dp[0] = true;
            for(int i = 1; i <= n; i++)
            {
                for(string x: wordDict)
                {
                    int xn = x.size();
                    if(i < xn) continue;
                    string s1 = s.substr(i - xn, xn);
                    if(s1 == x)
                        dp[i] = dp[i] || dp[i - xn];
                }
            }
            return dp[n];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    单词划分 II lc 140

    暴力dfs过了

    class Solution {
    public:
        vector ans;
        int n;
        vector wordBreak(string s, vector& wordDict) {
            n = s.size();
            string tmp = "";
            dfs(s, wordDict, 0, 0, tmp);
            return ans;
        }
        void dfs(string s, vector& wordDict, int start, int end, string tmp)
        {
            if(start == n)
            {
                tmp.pop_back();
                ans.push_back(tmp);
                tmp = "";
                return;
            }
            if(end >= n)
                return;
            string str = s.substr(start, end - start + 1);
            for(string x: wordDict)
            {
                if(str == x)
                    dfs(s, wordDict, end + 1, end + 1, tmp + x + " ");
            }
            dfs(s, wordDict, start, end + 1, tmp);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    什么时候要tmp =“” 没有影响,因为你回到之前的栈还是没加x的tmp而不是""

    11.6 猜数字大小系列

    猜数字大小 lc 374

    猜数字大小 II lc 375

    记忆化搜索:dfs(l, r)是指l, r区间内花费的最小值

    const int N = 210;
    int cache[N][N];
    class Solution {
    public:
        int getMoneyAmount(int n) {
            memset(cache, 0, sizeof cache);
            return dfs(1, n);
        }
        int dfs(int l, int r)
        {
            if(l >= r) return 0;
            if(cache[l][r] != 0) return cache[l][r];
            int c = INT_MAX;
            for(int i = l; i <= r; i++)
            {
                int cur = max(dfs(l, i - 1), dfs(i + 1, r)) + i;
                c = min(c, cur);
            }
            cache[l][r] = c;
            return c;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    11.7 摘樱桃 lc 741

    一道想不到的dp,题解第二次做的时候再写吧
    题解来啦!
    嗯嗯做第二遍是有意义的,第一遍很多地方没搞明白。

    首先要将一个人走两遍转换成两一个一块走走到最后,然后dp状态是两个人的坐标即dp[x1] [y1] [x2] [y2],由于两个人一块走,走的步数一样都是k=横纵坐标之和。所以可以简略成dp[k] [x1] [x2]。

    const int N = 55;
    class Solution {
    public:
        int cherryPickup(vector>& grid) {
            int dp[2 * N][N][N];
            for(int i = 0; i < 2 * N; i++)
            {
                for(int j = 0; j < N; j++)
                {
                    for(int k = 0; k < N; k++)
                    {
                        dp[i][j][k] = INT_MIN;
                    }
                }
            }
            dp[2][1][1] = grid[0][0]; //初始点位于(0, 0)即第一行第一列 1 + 1 = 2
            int n = grid.size();
            for(int k = 3; k <= 2 * n; k++) //最后位于n行n列 n + n = 2 * n
            {
                for(int r1 = 1; r1 <= n; r1++)
                {
                    for(int r2 = 1; r2 <= n; r2++)
                    {
                        //k是横纵坐标之和
                        //dp[k][r1][r2]表示走了k步,其中一人在r1行,另一人在r2行时摘到的最大樱桃数
                        int c1 = k - r1, c2 = k - r2;
                        if(c1 <= 0 || c1 > n || c2 <= 0 || c2 > n)
                            continue;
                        int p1 = grid[r1 - 1][c1 - 1], p2 = grid[r2 - 1][c2 - 1];
                        if(p1 == -1 || p2 == -1)
                            continue;
                        //上一次两个人的四个可能状态
                        int x1 = dp[k - 1][r1 - 1][r2], x2 = dp[k - 1][r1][r2 - 1];
                        int x3 = dp[k - 1][r1 - 1][r2 - 1], x4 = dp[k - 1][r1][r2];
                        int pre = max(max(x1, x2), max(x3, x4));
                        cout << pre << endl;
                        //如果两个人在同一个坐标处,加一个值就行
                        if(r1 == r2)
                            dp[k][r1][r2] = pre + p1;
                        else
                            dp[k][r1][r2] = pre + p1 + p2;
                    }
                }
            }
            return dp[2 * n][n][n] < 0 ? 0: dp[2 * n][n][n];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    11.8 只有两个键的键盘 lc650

    1. dfs

    class Solution {
    public:
        const int INF = 0x3f3f3f3f;
        int minSteps(int n) {
            return dfs(n, 1, 0);
        }
        int dfs(int n, int cur, int paste)
        {
            if(cur == n) return 0;
            if(cur > n) return INF;
            //选择copy
            int r1 = INF, r2 = INF;
            if(cur != paste) r1 = dfs(n, cur, cur) + 1;
            if(paste > 0)    r2 = dfs(n, cur + paste, paste) + 1;
            return min(r1, r2);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2. dfs + 记忆化

    const int N =  1100;
    int cache[N][N];
    class Solution {
    public:
        
        const int INF = 0x3f3f3f3f;
        int minSteps(int n) {
            memset(cache, -1, sizeof cache);
            return dfs(n, 1, 0);
        }
        int dfs(int n, int cur, int paste)
        {
            if(cur < N && cache[cur][paste] != -1) return cache[cur][paste];
            if(cur == n) return 0;
            if(cur > n) return INF;
            //选择copy
            int r1 = INF, r2 = INF;
            if(cur != paste) r1 = dfs(n, cur, cur) + 1;
            if(paste > 0)    r2 = dfs(n, cur + paste, paste) + 1;
            cache[cur][paste] = min(r1, r2);
            return cache[cur][paste];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.dp

    const int N = 1100;
    class Solution {
    public:
        int minSteps(int n) {
            int f[n + 1][n + 1];
            memset(f, 0x3f, sizeof f);
            f[1][0] = 0; f[1][1] = 1;
            for(int i = 2; i <= n; i++)
            {
                int m = INT_MAX;
                for(int j = 0; j <= i; j++)
                {
                    //上一次操作是paste j
                    f[i][j] = f[i - j][j] + 1;
                    //上一次操作是copy
                    m = min(m, f[i][j]);
                }
                //最后得到是i个字符,然后复制i个: 这个取决于再上一次也就是f[i - j][j] j <= i
                f[i][i] = m + 1;
            }
            int ans = INT_MAX;;
            for(int j = 0; j <= n; j++)
                ans = min(ans, f[n][j]);
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.数学

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DYnZzknF-1668236421449)(11_leetcode.assets/image-20221108104531781.png)]

    class Solution {
    public:
        int minSteps(int n) {
            int ans = 0;
            for(int i = 2; i <= n; i++)
            {
                while(n % i == 0)
                {
                    ans += i;
                    n /= i;
                }
            }
            if(n != 1) ans += n;
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    11.10 最长公共子序列系列LCS

    最长公共子序列裸题 lc1143

    返回text1和text2的最长公共子序列长度

    d[i] [j]表示前i个text1和前j个text2字符串的LCS长度

    • f[i][j] = f[i - 1][j - 1] + 1;
    • f[i][j] = max(f[i][j - 1], f[i - 1][j]);
    class Solution {
    public:
        
        int longestCommonSubsequence(string text1, string text2) {
            int n = text1.size(), m = text2.size();
            vector> f(n + 1, vector(m + 1, 0));
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                {
                    if(text1[i - 1] == text2[j - 1])
                        f[i][j] = f[i - 1][j - 1] + 1;
                    else
                        f[i][j] = max(f[i][j - 1], f[i - 1][j]);
                }
            }
            return f[n][m];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最长特殊子序列 LCS应用

    简单版:lc521

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jMQ0KJzP-1668236421450)(11_leetcode.assets/image-20221111114814671.png)]

    如果a和b长度不相等,那么那个长的就是最长特殊序列,因为短的里面没有

    如果a和b长度相等且a!= b也是同理,a整个长度在b中没有,答案就是二者的长度

    如果a== b那么没有

    第二版: lc522

    返回一个字符串数组中的最长特殊子序列

    使用每个字符串和其他字符串比较,看自身是否是其他所有字符串的最长公共子序列

    class Solution {
    public:
        int longestCommonSubsequence(string text1, string text2) {
            int n = text1.size(), m = text2.size();
            vector> f(n + 1, vector(m + 1, 0));
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                {
                    if(text1[i - 1] == text2[j - 1])
                        f[i][j] = f[i - 1][j - 1] + 1;
                    else
                        f[i][j] = max(f[i][j - 1], f[i - 1][j]);
                }
            }
            return f[n][m];
        }
        int findLUSlength(vector& s) {
            int n = s.size();
            int ans = -1;
            for(int i = 0; i < n; i++)
            {
                int sn = s[i].size();
                if(sn < ans)
                    continue;
                bool f = false;
                for(int j = 0; j < n; j++)
                {
                    if(i == j) continue;
                    if(longestCommonSubsequence(s[i], s[j]) == sn) 
                    {
                        f = true;
                        break;
                    }
                }
                if(!f) ans = max(ans, sn);
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    最短公共超序列 lc 1092

    以两个字符串(l = m, l = n)为公共子序列的最短字符串(l = k + m - k + n - k)

    思路就是:先找到最短公共子序列(l = k),然后在其基础上填填补补。

    但是没想到用dp去做,这算是一个dp求解具体方案数的一个问题。

    根据dp结果反推前面的情况是什么

    • dp[i] [j] == dp[i - 1] [j - 1]时,也即是s1[i] == s2[j],这时是公共子序列需要加上它
    • dp[i] [j] == dp[i - 1] [j]时,也就是使用了s1[i]
    • dp[i] [j] == dp[i] [j - 1]时,使用了s2[j]
    class Solution {
    public:
        string shortestCommonSupersequence(string text1, string text2) {
            int n = text1.size(), m = text2.size();
            int dp[n + 1][m + 1];
            memset(dp, 0, sizeof dp);
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                {
                    if(text1[i - 1] == text2[j - 1])
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                    else 
                        dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
            int i = n, j = m;
            string ans;
            while(i > 0 || j > 0)
            {
                if(i == 0)
                {
                    ans += text2[j - 1];
                    j--;
                }
                else if(j == 0)
                {
                    ans += text1[i - 1];
                    i--;
                }
                else if(text1[i - 1] == text2[j - 1])
                {
                    ans += text1[i - 1];
                    i--; j--;
                }
                else if(dp[i][j] == dp[i - 1][j])
                {
                    ans += text1[i - 1];
                    i--;
                }
                else
                {
                    ans += text2[j - 1];
                    j--;
                }
            }
            reverse(ans.begin(), ans.end());
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    11.10 最长递增子序列系列

    最长递增子序列裸题 lc300

    class Solution {
    public:
        int lengthOfLIS(vector& nums) {
            int n = nums.size();
            int dp[n];
            int ans = 0, ma = 1;
            for(int i = 1; i < n; i++)
            {
                dp[i] = 1;
                for(int j = i - 1; j >= 0; j--)
                {
                    if(nums[i] > nums[j])
                        dp[i] = max(dp[i], dp[j] + 1);
                }
                ma = max(ma, dp[i]);
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最长递增子序列的方案数 lc673

    class Solution {
    public:
        int findNumberOfLIS(vector& nums) {
            int n = nums.size();
            int dp[n], g[n];
            int ma = 1, ans = 0;;
            for(int i = 0; i < n; i++)
            {
                dp[i] = 1; g[i] = 1;
                for(int j = 0; j < i; j++)
                {
                    if(nums[i] > nums[j])
                    {
                        if(dp[i] < dp[j] + 1)
                        {
                            g[i] = g[j];
                            dp[i] = dp[j] + 1;
                        }
                        else if(dp[i] == dp[j] + 1)
                            g[i] += g[j];
                    }
                }
                ma = max(ma, dp[i]);
            }
            for(int i = 0; i < n; i++)
            {
                if(dp[i] == ma)
                    ans += g[i];
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    11.12 多米诺和托米诺平铺 lc 790

    深刻体会到定义dp数组的重要性,定义好dp数组答案就呼之欲出了

    class Solution {
    public:
        const int mod = 1e9 + 7;
        int numTilings(int n) {
            long long dp[n + 1][4];
            memset(dp, 0, sizeof dp);
            dp[0][3] = 1;
            for(int i = 1; i <= n; i++)
            {
                dp[i][0] = dp[i - 1][3] % mod;
                dp[i][1] = (dp[i - 1][2] + dp[i - 1][0]) % mod;
                dp[i][2] = (dp[i - 1][1] + dp[i - 1][0]) % mod;
                dp[i][3] = (dp[i - 1][0] + dp[i - 1][3] + dp[i - 1][1] + dp[i - 1][2]) % mod;
            }
            return dp[n][3];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    11.14 不同的子序列 lc115

    题目翻译一下是从s的子序列中选出t的个数

    class Solution {
    public:
        const int mod = 1e9 + 7;
        int numDistinct(string s, string t) {
            int sn = s.size(), tn = t.size();
            int dp[sn + 1][tn + 1];
            //dp[i][j]表示st中使用前i个去匹配t中的前j个用的个数
            memset(dp, 0, sizeof dp);
            for(int i = 0; i <= sn; i++)
                dp[i][0] = 1;
            for(int i = 1; i <= sn; i++)
            {
                for(int j = 1; j <= tn; j++)
                {
                    //匹配上了:我们可以选择用不用s[i]去匹配,不用是s[i]是后面可能还有,我们先不用s[i]
                    if(s[i - 1] == t[j - 1])
                        dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;
                    else
                        dp[i][j] = dp[i - 1][j];
                }
            }
            return dp[sn][tn];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    【vue设计与实现】渲染器 2-自定义渲染器
    从零开始的stable diffusion
    查找计算机视觉顶会CVPR/ECCV/ICCV论文的方法
    分类选择,最多五级
    SecureCRT SSH与FTP连接中文乱码
    每日三题 9.28
    QTabelWidget表格的插入、删除、更新、动态滑动条以及配合QFile进行表格内容的长期存储
    【Oracle】Oracle系列之三--Oracle字符集
    shell脚本 重试 分文件 多进程
    解决 React forwardRef 与 TypeScript 泛型组件冲突的问题
  • 原文地址:https://blog.csdn.net/studyyyyyyyyyy/article/details/127820504