刷题过程中遇到的正则表达式使用

最前面

华为机试:字符串通配符

三种解法

import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String in;
        while((in = br.readLine())!=null){
            String temp = br.readLine();
            //System.out.println(isMatch(temp,in));
            System.out.println(getOc(in,temp));
            //System.out.println(getOc(in,temp,0,0));
        }
    }
    public static boolean getOc(String s1,String s2,int p1,int p2){
        //递归求解
        //base case
        if (p1 == s1.length() && p2 == s2.length()){
            return true;
        }else if (p1 == s1.length() || p2 == s2.length()){
            return false;
        }
        //遇到'*'两种情况,要不就各跳过一个比较后面,要不就s2继续往后跳先不比较
        if (s1.charAt(p1) == '*'){
            return getOc(s1, s2, p1, p2+1) || getOc(s1, s2, p1+1, p2+1);
        //遇到'?'跟两个一样操作一样,直接指针都往后移一个继续比较
        }else if (s1.charAt(p1) == '?' || s1.charAt(p1) == s2.charAt(p2)){
            return getOc(s1, s2, p1+1, p2+1);
        }else {
            return false;
        }
    }//method end
    public static  boolean getOc(String in,String temp){
        //字符串通配符
            in = in.replaceAll("\\?", "[0-9A-Za-z]{1}");
            in = in.replaceAll("\\*", "[0-9A-Za-z]{0,}");//
            //in = in.replaceAll("\\.", "\\\\.");
            return temp.matches(in);
    }
    public static  boolean isMatch(String s, String p) {
         //动态规划
        if(s.length() == 0 && p.length() == 0)return true;
        if(s.length()!= 0 && p.length() == 0) return false;
        boolean [][] dp = new boolean [s.length()+1][p.length()+1];
        dp[0][0] = true;
        for(int j = 2; j <= p.length(); j++){
            if(p.charAt(j-1) == '*' && dp[0][j-2]){
                dp[0][j] = true;
            }
        }
        for(int i = 1; i <= s.length(); i++){
            for(int j = 1; j <= p.length(); j++){
                char a = s.charAt(i-1), b = p.charAt(j-1);
                if(a == b || b == '?'){
                    dp[i][j] = dp[i-1][j-1];
                }
                else if(b == '*'){
                    if(j>=2){
                         dp[i][j] = dp[i-1][j] || dp[i][j-2];
                    }
 
                       }
 
                else dp[i][j] = false;
            }
        }
        return dp[s.length()][p.length()];
    }

}  

剑指offer:53.表示数值的字符串

思路1

  1. +-号后面必定为数字或后面为.(-.123 = -0.123)
  2. +-号只出现在第一位或在eE的后一位
  3. .后面必定为数字或为最后一位(233. = 233.0)
  4. eE后面必定为数字或+-号

代码

public class Solution {
    public boolean isNumeric(char[] str) {

        boolean point = false, exp = false; // 标志小数点和指数

        for (int i = 0; i < str.length; i++) {
            if (str[i] == '+' || str[i] == '-') {
                // +-号后面必定为数字 或 后面为.(-.123 = -0.123)
                if (i + 1 == str.length || !(str[i + 1] >= '0' && str[i + 1] <= '9' || str[i + 1] == '.')) { 
                    return false;
                }
                // +-号只出现在第一位或eE的后一位
                if (!(i == 0 || str[i-1] == 'e' || str[i-1] == 'E')) { 
                    return false;
                }


            } else if (str[i] == '.') {
                if (point || exp || !(i + 1 < str.length && str[i + 1] >= '0' && str[i + 1] <= '9')) { // .后面必定为数字 或为最后一位(233. = 233.0)
                    return false;
                }
                point = true;

            } else if (str[i] == 'e' || str[i] == 'E') {
                if (exp || i + 1 == str.length || !(str[i + 1] >= '0' && str[i + 1] <= '9' || str[i + 1] == '+' || str[i + 1] == '-')) { // eE后面必定为数字或+-号
                    return false;
                }
                exp = true;

            } else if (str[i] >= '0' && str[i] <= '9') {


            } else {
                return false;
            }

        }
        return true;
    }
}

思路2

正则表达式大法

[]  : 字符集合
()  : 分组
?   : 重复 0 ~ 1 次
+   : 重复 1 ~ n 次
*   : 重复 0 ~ n 次
.   : 任意字符
\\. : 转义后的 .
\\d : 数字

{n} :必须匹配n次
{n,}:必须匹配n次或以上
{n,m}:匹配次数在n到m之间,包括边界

代码

public class Solution {
    public boolean isNumeric(char[] str) {
    if (str == null || str.length == 0)
        return false;
    return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
    }
}

剑指offer:52.正则表达式匹配

思路1 递归匹配

当模式中的第二个字符不是“*”时: 1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。 2、如果字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。

而当模式中的第二个字符是“”时: 可以有3种匹配方式: 1、模式后移2字符,相当于x被忽略;匹配0位 2、字符串后移1字符,模式后移2字符,x相当于只匹配一个字符; 3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为可以匹配多位;(包括匹配一位)

  1. 如果pattern[j] == ‘.’
  2. 如果str[i] == pattern[j]

以上两种情况都是i后移一位,然后j后移一位。

而当模式中的第二个字符是“*”时:

  1. 如果pattern[i] == '.' || str[i] == pattern[j],那么证明第一位相匹配,需要匹配0或多位 matchStr(str, i, pattern, j + 2) || matchStr(str, i + 1, pattern, j)
  2. 反之,j后移两位。

代码

public class Solution {
    public boolean matchStr(char[] str, int i, char[] pattern, int j) {
            if (i == str.length && j == pattern.length) { // 字符串和模式串都为空
                return true;
            } else if (j == pattern.length) { // 模式串为空
                return false;
            }

        boolean next = (j + 1 < pattern.length && pattern[j + 1] == '*');// 模式串下一个字符是'*'

        
        //当模式中的第二个字符是“*”时:
            if (next) {
                if (i < str.length && (pattern[j] == '.' || str[i] == pattern[j])) { 
                //匹配零位 & 匹配多位 (即使这一位相同或者是.,也有可能只匹配零位,匹配一位的情况包含在递归里了)
                    return matchStr(str, i, pattern, j + 2) || matchStr(str, i + 1, pattern, j);
                } else {
                    //匹配零位(当这一位不相同而且还不是.的时候,只能忽略了)
                    return matchStr(str, i, pattern, j + 2);
                }
            } 
        
        
        //当模式中的第二个字符不是“*”时:
        else {
                
                //匹配一位
                if (i < str.length && (pattern[j] == '.' || str[i] == pattern[j])) {
                    return matchStr(str, i + 1, pattern, j + 1);
                } else {
                    //匹配这一位失败
                    return false;
                }
            }
    }

    public boolean match(char[] str, char[] pattern) {
        return matchStr(str, 0, pattern, 0);
    }
}

思路2 动态规划

动态规划转移方程:

boolean dp[i][j]表示str的前i个和pattern的前j个能否匹配

第一个字符为str[0]

从pattern角度看过去

第j个字符,当前字符pattern[j-1]不是“*”:

如果 pattern[j-1] == '.' || str[i-1] == pattern[j-1]),dp[i][j] = dp[i-1][j-1]

否则,dp[i][j] =false;

第j个字符,当前字符pattern[j-1]是“*”:

比较pattern中前一字符pattern[j-2]与str[i-1]是否匹配

如果 pattern[j-2] == '.' || str[i-1] == pattern[j-2]),那么分两种情况

否则,

动态规划初始条件:

str为空且pattern为空,为真: dp[0][0] = true

str不为空且pattern为空,为假: dp[1..sn][0] = false

pattern[j-1]==’*‘时,dp[0,j]=dp[0,j-2]; 否则dp[0,j]=false;

public class Solution {
    public boolean judge(char p,char s){
        if(p=='.')return true;
        else return p==s;
    }
    public boolean match(char[] str, char[] pattern)
    {
        int pn=pattern.length;
        int sn=str.length;
        boolean[][] dp=new boolean[sn+1][pn+1];
        //初始化
        dp[0][0]=true;
        for(int j=1;j<=pn;j++){
            if(pattern[j-1]=='*')dp[0][j]=dp[0][j-2];
        }
        for(int i=1;i<=sn;i++){
            for(int j=1;j<=pn;j++){
                boolean now = (pattern[j-1] == '*');// 模式串当前字符是'*'
                if(!now){
                    if(judge(pattern[j-1],str[i-1]))
                        dp[i][j]=dp[i-1][j-1];
                }
                else{
                    if(judge(pattern[j-2],str[i-1]))
                        dp[i][j]=dp[i][j-2]|dp[i-1][j];
                    else dp[i][j]=dp[i][j-2];
                }
            }
        }
        return dp[sn][pn];
    }
}

思路3 和原正则表达式一致,使用库函数匹配

public class Solution {
    public boolean match(char[] str, char[] pattern)
    {
        return String.valueOf(str).matches(String.valueOf(pattern));
    }
}
import java.util.regex.*;
public class Solution {
    public boolean match(char[] str, char[] pattern)
    {
        String pat = String.valueOf(pattern);
        //把.替换为正则表达式的\w 用以匹配一个任意字符
        pat = pat.replaceAll("\\.", "\\\\w");
        Pattern pattern1 = Pattern.compile(pat);
        Matcher matcher = pattern1.matcher(String.valueOf(str));
        return matcher.matches();
    }
}

车轮滚滚 时间

import java.util.regex.*;

[]  : 字符集合
()  : 分组
?   : 重复 0 ~ 1 次
+   : 重复 1 ~ n 次
*   : 重复 0 ~ n 次
.   : 任意字符
\\. : 转义后的 .
\\d : 数字

{n} :必须匹配n次
{n,}:必须匹配n次或以上
{n,m}:匹配次数在n到m之间,包括边界

[+-]?\d*(\.\d+)?([eE][+-]?\d+)?

public String replaceAll(String regex, String replacement)

实现如下2个通配符:

*:匹配0个或以上的字符(字符由英文字母和数字0-9组成,不区分大小写。下同)

?:匹配1个字符

/*
public String replaceAll(String regex, String replacement)
参数
regex -- 匹配此字符串的正则表达式。

newChar -- 用来替换每个匹配项的字符串。

返回值
成功则返回替换的字符串,失败则返回原始字符串。
*/

public static  boolean getOc(String in,String temp){
        //字符串通配符
            in = in.replaceAll("\\?", "[0-9A-Za-z]{1}");
            in = in.replaceAll("\\*", "[0-9A-Za-z]{0,}");
            //in = in.replaceAll("\\.", "\\\\.");
            return temp.matches(in);
    }

        //@TEST
        String in = new String("te?t*.*");
        System.out.println(in.replaceAll("\\?", "[0-9A-Za-z]{1}"));
        //te[0-9A-Za-z]{1}t*.*
        System.out.println(in.replaceAll("\\*", "[0-9A-Za-z]{0,}"));
        //te?t[0-9A-Za-z]{0,}.[0-9A-Za-z]{0,}

        //注意事项 怕错 下面这种组合 表示
        System.out.println("".matches("[eE][+-]?"));//false
        System.out.println("e".matches("[eE][+-]?"));//true
        System.out.println("e+".matches("[eE][+-]?"));//true
        System.out.println("+".matches("[eE][+-]?"));//false

        System.out.println(".333".matches("\\.\\d+"));//true
        System.out.println("3".matches("\\.\\d+"));//false
        System.out.println("..3".matches("\\.\\d+"));//false

        System.out.println("7".matches("[0-9A-Za-z]{1}"));//true
        System.out.println("a".matches("[0-9A-Za-z]{1}"));//true
        System.out.println("Q".matches("[0-9A-Za-z]{1}"));//true

再来审视这个匹配

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。

规则抽象

  1. +-号后面必定为数字或后面为.(-.123 = -0.123)
  2. +-号只出现在第一位或在eE的后一位
  3. .后面必定为数字或为最后一位(233. = 233.0)
  4. eE后面必定为数字或+-号
public class Solution {
    public boolean isNumeric(char[] str) {
    if (str == null || str.length == 0)
        return false;
    return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
    }
}