LeetCode(CPP):一月刷完热题100(2)

LeetCode(CPP):一月刷完热题100(2),第1张

 包含了一些非热题100的题目 6.Z字形变换(mid)

思路:

根据题意,当我们在矩阵上填写字符时,会向下填写 r 个字符(行数),然后向右上继续填写 r-2 个字符,最后回到第一行,因此 Z 字形变换的周期 t=r+r-2=2r-2,每个周期会占用矩阵上的 1+r-2=r-1 列。来源:力扣(LeetCode)

在每个周期上,“Z”型变换认为“7”型为一周期,这样每一个周期中第一行和最后一行只有一个元素,其他行都有第一列和“/”型对角线上两个元素,所以枚举时,对每行的周期所包含的元素进行插入ans:所有行都插入第一个元素,非第一行和最后一行在条件(本位值未越界&&本位值加周期未越界)下则可以插入第二个元素。

逐行插入,可以直接返回ans。

class Solution {
public:
    string convert(string s, int numRows) {
        int n=s.length(),r=numRows;
        if(r==1||r>=n){return s;}
        
        string ans;//创建一个数组用于存储新的字符串数据
        int T=2*r-2;//周期T中元素个数,就是一个"Z"包含的列数
        for(int i=0;i0&&i0&&i

7. 整数反转(mid)

思路:

翻转首先想到了stack,但是负数还要讨论,另外比较占空间;

x%10,可以得到x的最高位数字,下一 *** 作将x/10,删除原数字x的最高位,为提取次高位做铺垫;

提取到高位数字后,把它们依次放到地位,循环执行(原先 i 位)*10+(原先 i-1 位);

代码很清楚。

class Solution{
public:
    int reverse(int x){
        int rev=0;
        while(x!=0)
        {
            if(revINT_MAX/10){return 0;}//越界返回0
            int digit=x%10;//提取x的最高位数字
            x/=10;//从高到低的下一位
            rev=rev*10+digit;//x的最高位数字作为rev的最低位数字
        }
        return rev;
    }
};

8.字符串转换整数(atoi)(mid)

思路: 

不如说是提取字符串中的数字(含负数)

本题使用了自动机DFS,也是我第一次见,我的理解就是把每个可能的情况列举出来。

class Automaton{//创建Automaton类
    string state="start";
    unordered_map> table={//使用map容器,前面是关键字,后面是对应的值
        {"start",{"start","signed","in_number","end"}},
        {"signed",{"end","end","in_number","end"}},
        {"in_number",{"end","end","in_number","end"}},
        {"end",{"end","end","end","end"}}
    };

    int get_col(char c){
        if(isspace(c)) return 0;//isspace,判断c是否为空
        if(c=='+'||c=='-') return 1;
        if(isdigit(c)) return 2;//isdigit,判断是否为数字
        return 3;
    }//0123分别用于找到table中vector容器内下标字符串

public:
    int sign = 1;
    long long ans =0;
    void get(char c){
        state=table[state][get_col(c)];
        if(state=="in_number"){
            ans= ans*10+c-'0';//-'0',是把'2'转化为2,'0'为ASCII值,字符数字转化为纯数字必须-'0'
            ans= sign==1? min(ans,(long long)INT_MAX) : min(ans,-(long long)INT_MIN);//判断是否超出范围?
        }
        else if(state=="signed"){
            sign=c=='+'?1:-1;
        }
    }
};
class Solution {
public:
    int myAtoi(string s) {
        Automaton automaton;//创建自动机对象,DFA,确定自动机
        for(char c:s)//遍历s中的字符,令其为c
           automaton.get(c);
        return automaton.sign*automaton.ans;
    }
};

9. 回文数(easy)

class Solution {
public:
    bool isPalindrome(int x) {
        // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,如果数字的最后一位是 0,为了使该数字为回文,
        // 则其第一位数字也应该是 0
        // 只有 0 满足这一属性
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }

        int reNum = 0;
        while (x > reNum) {
            reNum = reNum * 10 + x % 10;
            x /= 10;
        }

        // 当数字长度为奇数时,我们可以通过 reNum/10 去除处于中位的数字。
        // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,reNum = 123,
        // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
        return x == reNum || x == reNum / 10;//只要有一个是true,返回true
    }
};

10. 正则表达式(hard)


dp五部曲:


        设主串s的长度为m,设模式串p的长度为n;其中s只有小写字母,p有字母/./*
        1.状态定义:dp[i][j]为考虑s[0,i-1]与p[0,j-1]是否能匹配上,能匹配上就为true
        2.状态转移:若要求dp[i][j]就必须考虑到s[i-1]与p[j-1]
            2.1 当p[j-1]不是'*'时
                2.1.1 若s[i-1]==p[j-1]时,即p[j-1]为'a'-'z'且与s[i-1]相等,看dp[i-1][j-1]
                2.1.2 p[j-1] == '.'时,直接将'.'变成s[i-1],看dp[i-1][j-1]
                注意:这里的'.'是匹配一个字符,而不是一连串,如"a.b"->"axb"
            2.2 当p[j-1]是'*'时,主要看p[j-2]做判断
                2.2.1 p[j-2]为'a'-'z'并且p[j-2]==s[i-1],那么此时s继续往前看,p暂时不动
                    即:看dp[i-1][j]
                2.2.2 p[j-2]为'.',那么".*"可以变为"....."可以匹配s[i-1]前面的任何字符(万能串)
                    因此,直接看dp[i-1][j](或者直接返回true)
                2.2.3 剩下的就是p[j-2]为'a'-'z'且p[j-2]!=s[i-1],那么此时p的"x*"作废,看dp[i][j-2]
            这里:2.1.1与2.2.2可以看成一种情形:即s与p均前移一位
                2.2.1与2.2.2可以看成一种情形:即p不动s前移一位
        3.初始化:
            3.1 空的s
                3.1.1 空的p,空的s可以匹配空的p,因此dp[0][0]=true
                3.1.2 非空的p,空的s可能可以匹配非空的p,例如"a*",因此需要经过转移计算
            3.2 空的p
                3.2.1 空的s,同3.1.1
                3.2.2 非空的s,空的p不可能匹配非空的s,因此dp[i][0]=false,i∈[1,m]
            3.3 非空的s与非空的p:需要经过转移计算
            其中:3.1.1与3.2.2可以合并为dp[i][0]=i==0
        4.遍历顺序:显然是正序遍历
        5.返回形式:返回dp[m][n]就是考虑s[0,m-1]与p[0,n-1]是否能匹配上
       

受代码随想录carl的启发:

我个人觉得比较好理解的五部曲是这样:

1.明白数组dp[i]或dp[i][j]表示了什么含义;

2.dp数组的初始化,可能是dp[0],也可能是首行和首列(不同路径问题);

3.状态转移方程,dp[i][j]是怎么样与前面的状态dp[i-1][j-1]ordp[i-1][j]....相联系的,明白了这个就解决了一大半问题;

4.遍历顺序,从前往后还是从后往前,先干什么再干什么

5.输出结果,即最终的结果保存在哪里,dp[i-1][j-1]?

带入这道题中:

1.dp[i][j] 的含义:s 的前 i 个字符与 p 的前 j 个字符是否能够匹配,匹配为true,不能匹配为false

2.初始化: dp[0][0] = true; 这里还是比较绕的,没有具体的含义,就是没有任何字符就是匹配的,空==空。

3.状态转移方程:几个最基本的逻辑如下

(1)字母对字母,相同或不同;(2)字母对“.",则任意匹配,总是true;(3)字母对” * ",可以浪费也可以匹配多个前面的那一个元素

那么相应的状态转移就会是:(来自力扣)

(1)最简单的,如果此时的s[i]和p[j]相同,则比较前面一个字符,否则匹配不成功;

(2)" . "可以匹配一个任意元素:f[i][j]=f[i-1][j-1]

(3)" * "最复杂,可以对 p 的第 j−1 个字符匹配任意自然数次。在匹配 0 次的情况下,我们有

 在匹配 1,2,3, ⋯ 次的情况下,类似地我们有:

最终有:

 

 所以最终的状态转移方程:

 

其中matches(x,y) 判断两个字符是否匹配的辅助函数。只有当 y 是 . 或者 x 和 y 本身相同时,这两个字符才会匹配。

需要注意的是,运算是bool类型的,所以code采用 |= 的形式,两个量都不为true时即匹配失败。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/regular-expression-matching/solution/zheng-ze-biao-da-shi-pi-pei-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4.遍历顺序:从后往前;从p中找字符与s对比,先遍历s,在循环中再遍历p;

5.输出的结果放在dp[m][n],结尾处,如果dp[m][n]为true,即代表s 的前 m 个字符与 p 的前 n 个字符能够匹配

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size();
        int n = p.size();

        //用来判断是否在这一位置匹配上
        auto matches = [&](int i, int j) 
        {
            if (i == 0) {//s中没有元素
                return false;
            }
            if (p[j - 1] == '.') {
                return true;
            }
            return s[i - 1] == p[j - 1];
        };

        vector> dp(m + 1, vector(n + 1));
        dp[0][0] = true;//初始化
        for (int i = 0; i <= m; ++i) {//遍历
            for (int j = 1; j <= n; ++j) {
                if (p[j - 1] == '*') {
                    dp[i][j] |= dp[i][j - 2];
                    if (matches(i, j - 1)) {
                        dp[i][j] |= dp[i - 1][j];//状态转移方程
                    }
                }
                else {//不为*
                    if (matches(i, j)) {
                        dp[i][j] |= dp[i - 1][j - 1];//状态转移方程
                    }
                }
            }
        }
        return dp[m][n];//返回某个dp量
    }
};

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/728026.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)