Leetcode 6-10题

Z字形变换

将给定的字符串从上往下、从左到右进行\(Z\)字形排列为\(numRows\)行,在按层序读取为一个新的字符串

\(0123456789\)排列成4行为:

0     6       12
1   5 7    11 13
2 4   8 10    14
3     9       15

可以读取为\(06121571113...\)

这是一个找规律题,题目描述为\(Z\),但其实是一个倒\(N\)

从上述例子来看,0到5是一个循环,6到11是一个循环。这些循环都是一个等差序列。

并且只有第一行和最后一行是一个等差序列,其余都是两个等差序列交错出现,公差为\(2\times numRows-2\)

且第\(i\)行的第一个数为\(i\),另一个数为\(2\times numRows-2-i\)

特判:如果\(numRows=1\)​,那么只有一行,也就是原字符串。

string convert(string s, int numRows) {
    if(numRows == 1)    return s;           // 特判原字符串
    string ans = "";
    int p = 2 * numRows - 2, n = s.length();

    for(int i = 0; i < numRows; i ++) {     // 第i行
        if(i == 0 || i == numRows - 1) {
            for(int j = i; j < n; j += p)
                ans += s[j];
        }else {
            for(int j = i, k = p - i; j < n || k < n; j += p, k += p) {
                if(j < n)   ans += s[j];
                if(k < n)   ans += s[k];
            }
        }
    }
    return ans;
}

整数反转

给定32位有符号整数,返回翻转后的整数。若爆int则返回0,且不允许使用long long

要将1234翻转成4321,可以将个位数4提取出来,然后将原数除10得到123。循环这个过程,并将答案的410再加得到的个位数。

ans = ans * 10 + x % 10。这对于正数显然成立。

对于负数,由于在c++中-4 % 4 = -4-4 * 10 + -5 = -45,所以以上规则也成立。

可以这样判断得到的答案是否会爆int

\[INT\_MIN\leq10ans+x\%10 \leq INT\_MAX \\ \frac{INT\_MIN-x\%10}{10} \leq ans \leq \frac{INT\_MAX-x\%10}{10} \]

上式必须化简,否则会爆int

\(x\)为正数时,始终只要判断是否超过上限。\(x\)​为负数时,始终只要判断是否超过下限即可。

int reverse(int x) {
    int ans = 0;
    while(x) {
        if (x > 0 && ans > (INT_MAX - x % 10) / 10) return 0;
        if (x < 0 && ans < (INT_MIN - x % 10) / 10) return 0;
        ans = ans * 10 + x % 10;
        x /= 10;
    }
    return ans;
}

字符串转换整数

实现一个能将字符串转换为整数的函数。步骤如下:

  1. 去除前导空格;
  2. 检查正负号,如果没有默认为正数;
  3. 读入数字字符,直到到达末尾或到达非数字字符,并转换为整数;
  4. 若未读入数字,则答案为0,若超过32位整数范围,小于\(-2^{31}\)固定为\(-2^{31}\),大于\(2^{31}-1\)固定为\(2^{31}-1\)

和上一题类似,但是这题需要特判-2147483648的情况。

因为前面判断完正负号之后,后面的数值若为2147483648会超过int能表示的最大整数。

\[-10\times ans-p \geq INT\_MIN\\ \]

但是对于\(-10\times ans-p=INT\_MIN = -2147483648\)是正确的,但是\(10 \times ans+p=INT\_MAX=2147483648\)会爆int

所以需要特判这个情况,成为\(-10 \times ans -p = INT\_MIN\),但是这个式子不能化简,因为\(/10\)会过滤2147483647等情况。

int myAtoi(string s) {
    int k = 0, flag = 1, ans = 0;
    while(s[k] == ' ')  k ++;   // 去除前导空格

    if(s[k] == '-') flag = -1, k ++;
    else if(s[k] == '+')    k ++;

    while(s[k] >= '0' && s[k] <= '9') {
        int p = s[k] - '0';
        if(flag == 1 && ans > (INT_MAX - p) / 10)  return INT_MAX;

        // [-2147483648, 2147483647] 负数需要特判-2147483648的情况
        if(flag == -1 && -ans < (INT_MIN + p) / 10)  return INT_MIN;
        if(-10 * ans - p == INT_MIN)   return INT_MIN;

        ans = ans * 10 + p;
        k ++;
    }
    return ans * flag;
}

回文数

给定整数,判断是否为回文整数。

可以将整数转换为字符串,然后判断是否为回文串。

bool isPalindrome(int x) {
    string str = to_string(x), ss = to_string(x);
    reverse(ss.begin(), ss.end());
    return str == ss;
}

正则表达式匹配

给定两个字符串,问能否匹配,匹配规则如下:

  1. .匹配任意单个字符
  2. *匹配零个或多个前面的那一个元素

\(f(i,j)\)表示\(S[1..i]\)\(P[1..j]\)是否匹配。显然,\(f(i,0)\)不可能匹配(\(f(0,0)\)除外)。

在不考虑通配符的情况下,f(i,j)=f(i-1,j-1) && (S[i] = P[j])

考虑.通配符时,f(i,j)=f(i-1,j-1) && (P[j] == '.')

考虑*通配符时,若j >= 2,那么f(i, j) = f(i, j - 2)表示匹配零次前面的元素。

如果匹配一次前面的元素,那么f(i, j) = f(i - 1, j - 2) && (s[i] == p[j - 1] || p[j - 1] == '.')

如果匹配两次前面的元素,那么f(i, j) = f(i - 2, j - 2) && ...

也就是说f(i, j) = f(i, j - 2) + f(i - 1, j - 2) + f(i - 2, j - 2) + ...

类似完全背包的转移方式,又有f(i - 1, j) = f(i - 1, j - 2) + f(i - 2, j - 2) + f(i - 3, j - 2) + ...

所以f(i, j) = f(i - 1, j) + f(i, j - 2)

要使得S[i-1]P[j]能匹配,那么需要s[i]p[j - 1]能成功匹配,也就是说S的最后一个元素要能和P*之前的元素能匹配。

bool isMatch(string s, string p) {
    int m = s.length(), n = p.length();
    s = " " + s, p = " " + p;
    vector<vector<bool>> f(m + 10, vector<bool>(n + 10));

    f[0][0] = true;
    for(int i = 0; i <= m; i ++) {          // s[0..0]是可以和'a*'匹配的,所以从0开始
        for(int j = 1; j <= n; j ++) {      // 若P="",那么无法匹配,所以下标从1开始
            if(j + 1 <= n && p[j] != '*' && p[j + 1] == '*') continue;   // a*需要结合起来使用
            // 这里有一个特殊样例
            // s = "abc"
            // p = "a***abc"
            // 显然p[0][2] = true,因为a*可以匹配0次
            // 所以多个*视作一个*
            if(i > 0 && p[j] != '*')	
                f[i][j] = f[i - 1][j - 1] && (p[j] == '.' || p[j] == s[i]);
            else if(p[j] == '*')		// i必须要先大于0才能判断i-1
                f[i][j] = f[i][j - 2] || (i > 0 && f[i - 1][j] 
                                      && (p[j - 1] == '.' || p[j - 1] == s[i]));
        }
    }
    return f[m][n];
}
posted @ 2024-02-16 10:36  钰见梵星  阅读(4)  评论(0编辑  收藏  举报