力扣字符串练习题(正则表达式匹配、整数转罗马数字)

111 阅读1分钟

正则表达式匹配

来源:力扣(LeetCode) 链接:leetcode.cn/problems/re…

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

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

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

输入:s = "aa", p = "a"

输出:false

解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:s = "aa", p = "a*"

输出:true

解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:s = "ab", p = ".*"

输出:true

解释:".*" 表示可匹配零个或多个('*')任意字符('.')。

提示:

  • 1 <= s.length <= 20
  • 1 <= p.length <= 20
  • s 只包含从 a-z 的小写字母。
  • p 只包含从 a-z 的小写字母,以及字符 . 和 *。
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

代码

class Solution {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        
        boolean[][] dp = new boolean[m + 1][n + 1];
        dp[0][0] = true;
        
        // 初始化第一行,处理"*"
        for (int j = 1; j <= n; j++) {
            if (p.charAt(j - 1) == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }
        
        // 动态规划
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                char sc = s.charAt(i - 1);
                char pc = p.charAt(j - 1);
                
                if (sc == pc || pc == '.') {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (pc == '*') {
                    char prevPc = p.charAt(j - 2);
                    if (prevPc == sc || prevPc == '.') {
                        dp[i][j] = dp[i - 1][j] || dp[i][j - 2];
                    } else {
                        dp[i][j] = dp[i][j - 2];
                    }
                }
            }
        }
        
        return dp[m][n];
    }
}

思路分析

  1. 定义一个二维布尔数组dp,其中dp[i][j]表示字符串s的前i个字符与模式串p的前j个字符是否匹配。

  2. 初始化dp[0][0]true,表示空字符串与空模式串匹配。

  3. 初始化第一行,处理模式串以*开头的情况。如果当前字符为*,则可以将该*和前面的字符一起移除,所以dp[0][j]的值与dp[0][j-2]的值相同。

  4. 使用动态规划递推公式填充剩余的dp数组。

  5. 对于每个位置(i, j),判断当前字符和模式串的当前字符是否匹配。如果匹配,则dp[i][j]的值与dp[i-1][j-1]的值相同。

整数转罗马数字

来源:力扣(LeetCode) 链接:leetcode.cn/problems/in…

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符          数值

I             1

V             5

X             10

L             50

C             100

D             500

M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给你一个整数,将其转为罗马数字。

示例 1:

输入: num = 3

输出: "III"

示例 2:

输入: num = 4

输出: "IV"

示例 3:

输入: num = 9

输出: "IX"

示例 4:

输入: num = 58

输出: "LVIII"

解释: L = 50, V = 5, III = 3.

示例 5:

输入: num = 1994

输出: "MCMXCIV"

解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= num <= 3999

代码

class Solution {
    public String intToRoman(int num) {
        int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

        StringBuilder sb = new StringBuilder();
        int index = 0;

        while (num > 0) {
            int count = num / values[index];
            num %= values[index];

            for (int i = 0; i < count; i++) {
                sb.append(symbols[index]);
            }

            index++;
        }

        return sb.toString();
    }
}

思路分析

  1. 定义一个数组values和一个数组symbols,分别存储罗马数字的对应整数值和符号。

  2. 创建一个StringBuilder对象sb,用于存储转换后的罗马数字。

  3. 初始化变量index为0,用于表示当前处理的罗马数字的索引。

  4. 使用循环,当给定的整数num大于0时,进行以下操作:

    • 计算num除以values[index]的商,即表示当前罗马数字出现的次数。
    • numvalues[index]取余,得到剩余的数值。
    • 将当前罗马数字的符号根据出现次数添加到StringBuilder对象sb中。
    • index加1,转换到下一个罗马数字。
  5. 返回StringBuilder对象sb转换为字符串的结果。