您的位置:首页 > 职场人生

LeetCode10:Regular Expression Matching

2016-12-06 22:34 441 查看
原题目:

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:

public boolean isMatch(String s,String p){

//base case

if(p.length() == 0){

return s.length() == 0;

}

//special case

if(p.length() == 1){


//if the length of s is 0 return false

if(s.length() < 1){

	return false;

}


/*	if the first char of s and the first char of p is not the same

and the char of p is not '.',return false*/

else if((p.charAt(0) != s.charAt(0)) && (p.charAt(0) != '.')){

	return false;

}


//otherwise, compare the rest of the stirng of s and p

else{

	return isMatch(s.substring(1),p.substring(1));

}

}


//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));

}

}


//case2: when the second char of p is '*',complex case

else{

//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++;

}

return false;

}

}

[/code]





算法2:

public boolean isMatch(String s,String p){


int m = s.length();

int n = p.length();


if(m == 0 && n == 0)

return true;

if(n == 0){

return false;

}

boolean dp[][] = new boolean[m+1][n+1];  //boolean类型数组默认为false;

/*for(int k = 0; k < m; k++){

	for(int q = 0; q < n; q++){

System.out.println(dp[k][q]);

}

}*/


dp[0][0] = true;

for(int j = 0;j < n; j++){

if(p.charAt(j) == '*'){

	if(j > 0 && dp[0][j-1])

dp[0][j+1] = true;

	if(j < 1)

continue;

/*	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]对应的字符是‘*’那就意味着接下来的串就可以依次匹配下来,

       如果下面的字符一直重复,并且就是‘*’前面的那个字符)*/

	if(p.charAt(j-1) != '.'){

for(int i = 0; i < m;i++){

if(dp[i+1][j] || j > 0 && dp[i+1][j-1] || 

i > 0 && j > 0 && dp[i][j+1] && s.charAt(i) 

== s.charAt(i-1)&& s.charAt(i-1) == p.charAt(j-1))

	dp[i+1][j+1] = true;

}

}

	else{

int i = 0;

while(j > 0 && i < m && !dp[i+1][j-1] && !dp[i+1][j])

i++;

for(;i < m; i++){

dp[i+1][j+1] = true;

}

}


}

else{

	for(int i = 0;i < m;i++){

if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')

dp[i+1][j+1] = dp[i][j];

}

}

}

return dp[m][n];

}

[/code]





完整运行程序:

/**************************************************************

* Copyright (c) 2016

* All rights reserved.

* 版 本 号:v1.0

* 题目描述: 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).

* 输入描述:请输入第一个字符串:

*bbbbba

*请输入第二个字符串:

*b*a

* 程序输出:算法1:两个字符串是否匹配:false

*算法2:两个字符串是否匹配:true

*算法3:两个字符串是否匹配:true

* 问题分析:

* 算法描述:算法1:简单判断,分别考虑多种情况,但算法运行时间较长

* 算法2:利用动态规划

* 动态规划基本思想就是把我们计算过的历史信息记录下来,等到要用到的时候就直接使用,不用重新计算。

*在这个题里面,假设我们维护一个布尔数组res[i][j],代表s的前i个字符和p的前j

*个字符是否匹配(注意这里res的维度是s.length()+1,p.length()+1)。

* 完成时间:2016-11-23

***************************************************************/


package org.GuoGuoFighting.LeetCode010;


import java.util.Scanner;


/*算法1:错误的算法,无法得到正确结果*/

class SolutionMethod1{

public boolean isMatch(String s,String p){

if(s == null || p == null){

return false;

}


int slen = s.length();

int plen = p.length();

int i = slen-1;

int j = plen-1;

//boolean flag = false;

while(i > 0 && j > 0){

while(s.charAt(i) == s.charAt(i-1)){

	i--;

}

if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){

	if(i > 0 && j >0){

i--;

j--;

System.out.println("i=" + i + "j=" + j);

return isMatch(s.substring(0, i), p.substring(0, j));

//return MatchFunction(s,i, p,j);

}

	else if(i == 0 && j == 0){

return true;

}

	else if(s.charAt(i) == s.charAt(i-1)){

i--;

}

}

else if(p.charAt(j) == '*' ){

	j--;

	return isMatch(s.substring(0, i), p.substring(0, j));

}

else{

	return false;

}

}

if( i == 0 && j== 0 && s.charAt(i)== p.charAt(j)){

return true;


}

else

return false;

}

}


class SolutionMethod2{

public boolean isMatch(String s,String p){

//base case

if(p.length() == 0){

return s.length() == 0;

}

//special case

if(p.length() == 1){


//if the length of s is 0 return false

if(s.length() < 1){

	return false;

}


/*	if the first char of s and the first char of p is not the same

and the char of p is not '.',return false*/

else if((p.charAt(0) != s.charAt(0)) && (p.charAt(0) != '.')){

	return false;

}


//otherwise, compare the rest of the stirng of s and p

else{

	return isMatch(s.substring(1),p.substring(1));

}

}


//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));

}

}


//case2: when the second char of p is '*',complex case

else{

//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++;

}

return false;

}

}

}


class SolutionMethod3{

public boolean isMatch(String s,String p){


int m = s.length();

int n = p.length();


if(m == 0 && n == 0)

return true;

if(n == 0){

return false;

}

boolean dp[][] = new boolean[m+1][n+1];  //boolean类型数组默认为false;

/*for(int k = 0; k < m; k++){

	for(int q = 0; q < n; q++){

System.out.println(dp[k][q]);

}

}*/


dp[0][0] = true;

for(int j = 0;j < n; j++){

if(p.charAt(j) == '*'){

	if(j > 0 && dp[0][j-1])

dp[0][j+1] = true;

	if(j < 1)

continue;

/*	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]对应的字符是‘*’那就意味着接下来的串就可以依次匹配下来,

       如果下面的字符一直重复,并且就是‘*’前面的那个字符)*/

	if(p.charAt(j-1) != '.'){

for(int i = 0; i < m;i++){

if(dp[i+1][j] || j > 0 && dp[i+1][j-1] || 

i > 0 && j > 0 && dp[i][j+1] && s.charAt(i) 

== s.charAt(i-1)&& s.charAt(i-1) == p.charAt(j-1))

	dp[i+1][j+1] = true;

}

}

	else{

int i = 0;

while(j > 0 && i < m && !dp[i+1][j-1] && !dp[i+1][j])

i++;

for(;i < m; i++){

dp[i+1][j+1] = true;

}

}


}

else{

	for(int i = 0;i < m;i++){

if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')

dp[i+1][j+1] = dp[i][j];

}

}

}

return dp[m][n];

}

}


public class RegularExpressionMatching {

public static void main(String[] args){

Scanner scanner = new Scanner(System.in);

System.out.println("请输入第一个字符串:");

String s = scanner.nextLine();

System.out.println("请输入第二个字符串:");

String p = scanner.nextLine();

scanner.close();


SolutionMethod1 solution1 = new SolutionMethod1();

System.out.println("算法1:两个字符串是否匹配:" + solution1.isMatch(s, p));


SolutionMethod2 solution2 = new SolutionMethod2();

System.out.println("算法2:两个字符串是否匹配:" + solution2.isMatch(s, p));


SolutionMethod3 solution3 = new SolutionMethod3();

System.out.println("算法3:两个字符串是否匹配:"+ solution3.isMatch(s, p));

}

}

[/code]

程序运行结果:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 面试题 leetcode