今日标题链接:正则表达式匹配

正则表达式匹配

难度:较难

描绘

请完成一个函数用来匹配包括’.’和’*’的正则表达式。

1.形式中的字符‘.’表明任意一个字符

2.形式中的字符’*’表明它前面的字符能够呈现任意次(包括0次)。

在本题中,匹配是指字符串的一切字符匹配整个形式。例如,字符串”aaa”与形式”a.a”和”abaca”匹配,可是与”aa.a”和”ab*a”均不匹配

数据范围

1.str 只包括从a-z的小写字母。

2.pattern 只包括从a-z的小写字母以及字符.和,无接连的’‘。

3.0≤str.length≤26

4.0≤pattern.length≤26

举例

每日一刷《剑指offer》字符串篇之正则表达式匹配

解题思路

本题不难理解,可是匹配过程中需求考虑的状况比较多,需求谨慎地考虑到每一种状况。

首先,咱们剖析如何匹配一个字符,当用一个字符去和形式串中的字符匹配时,假如形式中的字符是.,那么任何字符都能够匹配:或者,假如两个字符相同,那么能够匹配,接着再去匹配下一个字符。

相对来说,当形式串的第二个字符不是 * 时,问题比较简单:若字符串的榜首个字符和形式串的榜首个字符匹配时,字符害和形式串指针都向后移动一个字符,然后匹配剩余的字符串和形式。假如榜首个字符不匹配,那么就能够直接回来false。

当形式串的第二个字符是 * 时,状况就比较复杂,由于可能有多种不同的匹配方式:

  • 不管榜首个字符是否持平,形式串向后移动两个字符,相当于 * 和它前面的字符被忽略,由于 * 能够代表前面的字符呈现0次。
  • 假如形式串榜首个字符和字符串榜首个字符匹配,则字符串向后移动一个字符,比较下一位,而形式串此时有两种状况:形式串向后移动两个字符,也能够坚持形式不变 (由于 * 能够代表前面的字符呈现多次)。

如下图所示,当匹配进入状况2并目字符串的字符是a时,有两种选择: 进入状况3或者坚持状况2。

每日一刷《剑指offer》字符串篇之正则表达式匹配

完成代码(java)

public class Solution {
    public boolean match(char[] str, char[] pattern){
        /*
        思路:比较前两个字符,递归比较
        */
        if(str==null || pattern==null)
            return false;
        return match(str,0,pattern,0);
    }
    public boolean match(char[] str,int i,char[] pattern,int j){
        if(i==str.length && j==pattern.length)//都为空
            return true;
        if(i<str.length && j==pattern.length)//形式串为空
            return false;
        //以下j一定是<len
        if(j+1<pattern.length && pattern[j+1]=='*'){ //第二个字符是*
            if((i<str.length && str[i]==pattern[j]) ||(i<str.length && pattern[j]=='.') ) //榜首个字符持平,有三种状况
                return match(str,i,pattern,j+2) || match(str,i+1,pattern,j+2) || match(str,i+1,pattern,j);
                //别离代表匹配0个,1个和多个 
            else //榜首个字符不等
                return match(str,i,pattern,j+2);
        }else{     //第二个字符不是*
            if((i<str.length && str[i]==pattern[j]) || ( pattern[j]=='.'&& i< str.length))
                return match(str,i+1,pattern,j+1);
            else
                return false;
        }
    }
}

学习完本题的思路你能够处理如下标题:

BM65 最长公共子序列(二)

最长公共子序列(二)

难度:中等

描绘

给定两个字符串str1和str2,输出两个字符串的最长公共子序列。假如最长公共子序列为空,则回来”-1″。目前给出的数据,只是会存在一个最长的公共子序列

举例

每日一刷《剑指offer》字符串篇之正则表达式匹配

解题思路

动态规划+递归获取;标题要求获取最长公共子序列,咱们肯定要先知道最长到底是多长,因此肯定要先求最长公共子序列的长度,然后根据这个长度获取这个子序列。(注意:子序列不是子串,子串要求一切字符必须接连,子序列不要求接连,只需求相对方位不变)

  • 首先对于动态规划,需求清晰状况: 当时处理到的 s1 和 s2 别离的第 i 和第 j 个字符

  • 界说状况数组:dp[i][j]表明从左到右,当处理到s1的第i个元素和s2的第j个元素时的公共子序列

  • 状况初始化,即当i==0或j==0的状况,dp[i][j]为””,由于空字符串没有公共子序列

  • 状况搬运

    • 当时字符持平,则增加成果,i 和 j 指针右移,状况搬运方程为:dp[i][j] = dp[i-1][j-1] + s1.charAt(i-1);
    • 当时字符不持平,则还需求分两种状况,取长度较长的状况,状况搬运方程为: dp[i][j] = dp[i-1][j].length() > dp[i][j-1].length() ? dp[i-1][j] : dp[i][j-1]

完成代码(java)

import java.util.*;
public class Solution {
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    public String LCS (String s1, String s2) {
        int len1 = s1.length(), len2 = s2.length();
        // 清晰状况: 当时需求处理的s1和s2别离前i和前j个元素
        // dp[i][j]表明从左到右,当处理到s1的第i个元素和s2的第j个元素时的公共子序列
        String[][] dp = new String[len1 + 1][len2 + 1];
        // base case:当i==0或j==0的状况,dp[i][j]为"",由于空字符串没有公共子序列
        for(int i = 0; i <= len1; i++) {
            // j == 0
            dp[i][0] = "";
        }
        for(int j = 0; j <= len2; j++) {
            // i == 0
            dp[0][j] = "";
        }
        // 状况搬运
        for(int i = 1; i <= len1; i++) {
            for(int j = 1; j <= len2; j++) {
                // 当时字符持平,则增加成果
                if(s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i-1][j-1] + s1.charAt(i-1);
                } else {
                    // 当时字符不持平,则还需求分两种状况,取长度较长的状况
                    dp[i][j] = dp[i-1][j].length() > dp[i][j-1].length() ? dp[i-1][j] : dp[i][j-1];
                }
            }
        }
        return dp[len1][len2] == "" ? "-1" : dp[len1][len2];
    }
}

学习完本题的思路你能够处理如下标题: BM71.最长上升子序列(一)

最长上升子序列(一)

难度:中等

描绘

给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。

所谓子序列,指一个数组删掉一些数(也能够不删)之后,构成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。

咱们界说一个序列是 严格上升 的,当且仅当该序列不存在两个下标 ii 和 jj 满意 i<ji<j 且 arri≥arrjarri​≥arrj​。

举例

每日一刷《剑指offer》字符串篇之正则表达式匹配

解题思路

动态规划;

  • 状况界说:dp[i]dp[i]dp[i]表明以下标i结束的最长上升子序列的长度。
  • 状况初始化:以任意下标结束的上升子序列长度不小于1,故初始化为1。
  • 状况搬运:遍历数组中一切的数,再遍历当时数之前的一切数,只需前面某个数小于当时数,则要么长度在之前基础上加1,要么坚持不变,取两者中的较大者。即dp[i]=Math.max(dp[i],dp[j]+1)dp[i]=Math.max(dp[i],dp[j]+1)dp[i]=Math.max(dp[i],dp[j]+1)。

完成代码(java)

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接回来方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        int n=arr.length;
        //特殊请款判断
        if(n==0) return 0;
        //dp[i]表明以下标i结束的最长上升子序列长度
        int[] dp=new int[n];
        for(int i=0;i<n;i++){
            //初始化为1
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(arr[i]>arr[j]){
                    //只需前面某个数小于当时数,则要么长度在之前基础上加1,要么坚持不变,取较大者
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
        }
        //回来一切可能中的最大值
        return Arrays.stream(dp).max().getAsInt();
    }
}