LeetCode10:Regular Expression Matching

原题目:

Implement regular expression matching with support for '.' and '*'.
 
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true
匹配字符串,“.”能够代替所有字符,“*”代表前一个或前多个字符,例如,b*,可以代表0个b,也可以代表bbbb多个b,这便是难点。

算法分析:
算法1:
如果s的第一个字符和p的第一个字符不相等并且p的第一个字符不是'.',直接返回false
情况1:当p的第二个字符不是‘*’,属于简单情况,如果*前面的字符和s的字符不相等并且p的前一个字符不是'.',则直接返回false。其中,利用substring(1)来截掉第一个字符(substring(index)用来从头开始截掉Index长度的字符串
//case1:when the second char of p is not '*',easy case.
        if(p.charAt(1) != '*'){
            if(s.length() < 1){
                return false;
            }
            if((p.charAt(0) != s.charAt(0)) &&(p.charAt(0) != '.')){
                return false;
            }
            else{
                return isMatch(s.substring(1),p.substring(1));
            }
        }  
情况2:当p的第二个字符是“*”的时候,复杂情况:
         要分0代表0个前导字符或者1个或多个前导字符的情况。
          //when the '*' stands for 0 preceding element
            if(isMatch(s,p.substring(2))){            //if * its after element could match s,then ,*replace before 0 element
                return true;
            }
            /*when the '*' stands for 1 or more preceding element,
            try every possible number*/
            int i = 0;
            while(i < s.length() && (s.charAt(i) == p.charAt(0) || 
                    p.charAt(0) == '.')){
                if(isMatch(s.substring(i+1),p.substring(2))){
                    return true;
                }
                i++;
            }
算法:2:
利用动态规划。 动态规划基本思想就是把我们计算过的历史信息记录下来,等到要用到的时候就直接使用,不用重新计算。在这个题里面,假设我们维护一个布尔数组res[i][j],代表s的前i个字符和p的前j个字符是否匹配(注意这里res的维度是s.length()+1,p.length()+1)。递推公式跟上面类似,分三种种情况:
(1)p[j+1]不是'*'。情况比较简单,只要判断如果当前s的i和p的j上的字符一样(如果有p在j上的字符是'.',也是相同),并且res[i][j]==true,则res[i+1][j+1]也为true,res[i+1][j+1]=false;
(2)p[j+1]是'*',但是p[j]!='.'。那么只要以下条件有一个满足即可对res[i+1][j+1]赋值为true:
    1)res[i+1][j]为真('*'只取前面字符一次);
    2)res[i+1][j-1]为真('*'前面字符一次都不取,也就是忽略这两个字符);
    3)res[i][j+1] && s[i]==s[i-1] && s[i-1]==p[j-1](这种情况是相当于i从0到s.length()扫过来,如果p[j+1]对应的字符是‘*’那就意味着接下来的串就可以依次匹配下来,如果下面的字符一直重复,并且就是‘*’前面的那个字符)。
(3)p[j+1]是'*',并且p[j]=='.'。因为".*"可以匹配任意字符串,所以在前面的res[i+1][j-1]或者res[i+1][j]中只要有i+1是true,那么剩下的res[i+1][j+1],res[i+2][j+1],...,res[s.length()][j+1]就都是true了。
这道题有个很重要的点,就是实现的时候外层循环应该是p,然后待匹配串s内层循环扫过来。




LeetCode提交源码:
算法1:
   
   
  1. public boolean isMatch(String s,String p){
  2. //base case
  3. if(p.length() == 0){
  4. return s.length() == 0;
  5. }
  6. //special case
  7. if(p.length() == 1){
  8. //if the length of s is 0 return false
  9. if(s.length() < 1){
  10. return false;
  11. }
  12. /* if the first char of s and the first char of p is not the same
  13. and the char of p is not '.',return false*/
  14. else if((p.charAt(0) != s.charAt(0)) && (p.charAt(0) != '.')){
  15. return false;
  16. }
  17. //otherwise, compare the rest of the stirng of s and p
  18. else{
  19. return isMatch(s.substring(1),p.substring(1));
  20. }
  21. }
  22. //case1:when the second char of p is not '*',easy case.
  23. if(p.charAt(1) != '*'){
  24. if(s.length() < 1){
  25. return false;
  26. }
  27. if((p.charAt(0) != s.charAt(0)) &&(p.charAt(0) != '.')){
  28. return false;
  29. }
  30. else{
  31. return isMatch(s.substring(1),p.substring(1));
  32. }
  33. }
  34. //case2: when the second char of p is '*',complex case
  35. else{
  36. //when the '*' stands for 0 preceding element
  37. if(isMatch(s,p.substring(2))){ //if * its after element could match s,then ,*replace before 0 element
  38. return true;
  39. }
  40. /*when the '*' stands for 1 or more preceding element,
  41. try every possible number*/
  42. int i = 0;
  43. while(i < s.length() && (s.charAt(i) == p.charAt(0) ||
  44. p.charAt(0) == '.')){
  45. if(isMatch(s.substring(i+1),p.substring(2))){
  46. return true;
  47. }
  48. i++;
  49. }
  50. return false;
  51. }
  52. }
 
算法2:
   
   
  1. public boolean isMatch(String s,String p){
  2. int m = s.length();
  3. int n = p.length();
  4. if(m == 0 && n == 0)
  5. return true;
  6. if(n == 0){
  7. return false;
  8. }
  9. boolean dp[][] = new boolean[m+1][n+1]; //boolean类型数组默认为false;
  10. /*for(int k = 0; k < m; k++){
  11. for(int q = 0; q < n; q++){
  12. System.out.println(dp[k][q]);
  13. }
  14. }*/
  15. dp[0][0] = true;
  16. for(int j = 0;j < n; j++){
  17. if(p.charAt(j) == '*'){
  18. if(j > 0 && dp[0][j-1])
  19. dp[0][j+1] = true;
  20. if(j < 1)
  21. continue;
  22. /* p[j+1]是'*',但是p[j]!='.'。那么只要以下条件有一个满足即可对res[i+1][j+1]赋值为true:
  23. 1)res[i+1][j]为真('*'只取前面字符一次);
  24. 2)res[i+1][j-1]为真('*'前面字符一次都不取,也就是忽略这两个字符);
  25. 3)res[i][j+1] && s[i]==s[i-1] && s[i-1]==p[j-1](这种情况是相当于i从0到s.length()扫过来,
  26. 如果p[j+1]对应的字符是‘*’那就意味着接下来的串就可以依次匹配下来,
  27. 如果下面的字符一直重复,并且就是‘*’前面的那个字符)*/
  28. if(p.charAt(j-1) != '.'){
  29. for(int i = 0; i < m;i++){
  30. if(dp[i+1][j] || j > 0 && dp[i+1][j-1] ||
  31. i > 0 && j > 0 && dp[i][j+1] && s.charAt(i)
  32. == s.charAt(i-1)&& s.charAt(i-1) == p.charAt(j-1))
  33. dp[i+1][j+1] = true;
  34. }
  35. }
  36. else{
  37. int i = 0;
  38. while(j > 0 && i < m && !dp[i+1][j-1] && !dp[i+1][j])
  39. i++;
  40. for(;i < m; i++){
  41. dp[i+1][j+1] = true;
  42. }
  43. }
  44. }
  45. else{
  46. for(int i = 0;i < m;i++){
  47. if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')
  48. dp[i+1][j+1] = dp[i][j];
  49. }
  50. }
  51. }
  52. return dp[m][n];
  53. }
 

完整运行程序:

   
   
  1. /**************************************************************
  2. * Copyright (c) 2016
  3. * All rights reserved.
  4. * 版 本 号:v1.0
  5. * 题目描述: Regular Expression Matching
  6. * Implement regular expression matching with support for '.' and '*'.
  7. * '.' Matches any single character.
  8. * '*' Matches zero or more of the preceding element.
  9. * The matching should cover the entire input string (not partial).
  10. * 输入描述:请输入第一个字符串:
  11. * bbbbba
  12. * 请输入第二个字符串:
  13. * b*a
  14. * 程序输出:算法1:两个字符串是否匹配:false
  15. * 算法2:两个字符串是否匹配:true
  16. * 算法3:两个字符串是否匹配:true
  17. * 问题分析:
  18. * 算法描述:算法1:简单判断,分别考虑多种情况,但算法运行时间较长
  19. * 算法2:利用动态规划
  20. * 动态规划基本思想就是把我们计算过的历史信息记录下来,等到要用到的时候就直接使用,不用重新计算。
  21. * 在这个题里面,假设我们维护一个布尔数组res[i][j],代表s的前i个字符和p的前j
  22. * 个字符是否匹配(注意这里res的维度是s.length()+1,p.length()+1)。
  23. * 完成时间:2016-11-23
  24. ***************************************************************/
  25. package org.GuoGuoFighting.LeetCode010;
  26. import java.util.Scanner;
  27. /*算法1:错误的算法,无法得到正确结果*/
  28. class SolutionMethod1{
  29. public boolean isMatch(String s,String p){
  30. if(s == null || p == null){
  31. return false;
  32. }
  33. int slen = s.length();
  34. int plen = p.length();
  35. int i = slen-1;
  36. int j = plen-1;
  37. //boolean flag = false;
  38. while(i > 0 && j > 0){
  39. while(s.charAt(i) == s.charAt(i-1)){
  40. i--;
  41. }
  42. if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){
  43. if(i > 0 && j >0){
  44. i--;
  45. j--;
  46. System.out.println("i=" + i + "j=" + j);
  47. return isMatch(s.substring(0, i), p.substring(0, j));
  48. //return MatchFunction(s,i, p,j);
  49. }
  50. else if(i == 0 && j == 0){
  51. return true;
  52. }
  53. else if(s.charAt(i) == s.charAt(i-1)){
  54. i--;
  55. }
  56. }
  57. else if(p.charAt(j) == '*' ){
  58. j--;
  59. return isMatch(s.substring(0, i), p.substring(0, j));
  60. }
  61. else{
  62. return false;
  63. }
  64. }
  65. if( i == 0 && j== 0 && s.charAt(i)== p.charAt(j)){
  66. return true;
  67. }
  68. else
  69. return false;
  70. }
  71. }
  72. class SolutionMethod2{
  73. public boolean isMatch(String s,String p){
  74. //base case
  75. if(p.length() == 0){
  76. return s.length() == 0;
  77. }
  78. //special case
  79. if(p.length() == 1){
  80. //if the length of s is 0 return false
  81. if(s.length() < 1){
  82. return false;
  83. }
  84. /* if the first char of s and the first char of p is not the same
  85. and the char of p is not '.',return false*/
  86. else if((p.charAt(0) != s.charAt(0)) && (p.charAt(0) != '.')){
  87. return false;
  88. }
  89. //otherwise, compare the rest of the stirng of s and p
  90. else{
  91. return isMatch(s.substring(1),p.substring(1));
  92. }
  93. }
  94. //case1:when the second char of p is not '*',easy case.
  95. if(p.charAt(1) != '*'){
  96. if(s.length() < 1){
  97. return false;
  98. }
  99. if((p.charAt(0) != s.charAt(0)) &&(p.charAt(0) != '.')){
  100. return false;
  101. }
  102. else{
  103. return isMatch(s.substring(1),p.substring(1));
  104. }
  105. }
  106. //case2: when the second char of p is '*',complex case
  107. else{
  108. //when the '*' stands for 0 preceding element
  109. if(isMatch(s,p.substring(2))){ //if * its after element could match s,then ,*replace before 0 element
  110. return true;
  111. }
  112. /*when the '*' stands for 1 or more preceding element,
  113. try every possible number*/
  114. int i = 0;
  115. while(i < s.length() && (s.charAt(i) == p.charAt(0) ||
  116. p.charAt(0) == '.')){
  117. if(isMatch(s.substring(i+1),p.substring(2))){
  118. return true;
  119. }
  120. i++;
  121. }
  122. return false;
  123. }
  124. }
  125. }
  126. class SolutionMethod3{
  127. public boolean isMatch(String s,String p){
  128. int m = s.length();
  129. int n = p.length();
  130. if(m == 0 && n == 0)
  131. return true;
  132. if(n == 0){
  133. return false;
  134. }
  135. boolean dp[][] = new boolean[m+1][n+1]; //boolean类型数组默认为false;
  136. /*for(int k = 0; k < m; k++){
  137. for(int q = 0; q < n; q++){
  138. System.out.println(dp[k][q]);
  139. }
  140. }*/
  141. dp[0][0] = true;
  142. for(int j = 0;j < n; j++){
  143. if(p.charAt(j) == '*'){
  144. if(j > 0 && dp[0][j-1])
  145. dp[0][j+1] = true;
  146. if(j < 1)
  147. continue;
  148. /* p[j+1]是'*',但是p[j]!='.'。那么只要以下条件有一个满足即可对res[i+1][j+1]赋值为true:
  149. 1)res[i+1][j]为真('*'只取前面字符一次);
  150. 2)res[i+1][j-1]为真('*'前面字符一次都不取,也就是忽略这两个字符);
  151. 3)res[i][j+1] && s[i]==s[i-1] && s[i-1]==p[j-1](这种情况是相当于i从0到s.length()扫过来,
  152. 如果p[j+1]对应的字符是‘*’那就意味着接下来的串就可以依次匹配下来,
  153. 如果下面的字符一直重复,并且就是‘*’前面的那个字符)*/
  154. if(p.charAt(j-1) != '.'){
  155. for(int i = 0; i < m;i++){
  156. if(dp[i+1][j] || j > 0 && dp[i+1][j-1] ||
  157. i > 0 && j > 0 && dp[i][j+1] && s.charAt(i)
  158. == s.charAt(i-1)&& s.charAt(i-1) == p.charAt(j-1))
  159. dp[i+1][j+1] = true;
  160. }
  161. }
  162. else{
  163. int i = 0;
  164. while(j > 0 && i < m && !dp[i+1][j-1] && !dp[i+1][j])
  165. i++;
  166. for(;i < m; i++){
  167. dp[i+1][j+1] = true;
  168. }
  169. }
  170. }
  171. else{
  172. for(int i = 0;i < m;i++){
  173. if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')
  174. dp[i+1][j+1] = dp[i][j];
  175. }
  176. }
  177. }
  178. return dp[m][n];
  179. }
  180. }
  181. public class RegularExpressionMatching {
  182. public static void main(String[] args){
  183. Scanner scanner = new Scanner(System.in);
  184. System.out.println("请输入第一个字符串:");
  185. String s = scanner.nextLine();
  186. System.out.println("请输入第二个字符串:");
  187. String p = scanner.nextLine();
  188. scanner.close();
  189. SolutionMethod1 solution1 = new SolutionMethod1();
  190. System.out.println("算法1:两个字符串是否匹配:" + solution1.isMatch(s, p));
  191. SolutionMethod2 solution2 = new SolutionMethod2();
  192. System.out.println("算法2:两个字符串是否匹配:" + solution2.isMatch(s, p));
  193. SolutionMethod3 solution3 = new SolutionMethod3();
  194. System.out.println("算法3:两个字符串是否匹配:"+ solution3.isMatch(s, p));
  195. }
  196. }


程序运行结果:
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值