lintcode上面的一个动态规划例子

         lintcode上面有一个192题,通配符匹配问题,描述是这样的:

 Test192 : 判断两个可能包含通配符“?”和“*”的字符串是否匹配。匹配规则如下:
 
 '?' 可以匹配任何单个字符。 '*' 可以匹配任意字符串(包括空字符串)。
 
 两个串完全匹配才算匹配成功。
 
 函数接口如下: bool isMatch(const char *s, const char *p)
 
 样例 一些例子:
 isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false

        这个题看到以后我第一时间想到的是用递归,思路是这样的:

         字符串s和匹配串p挨个做字符的匹配,当p出现*的时候,有两种情况:

       (1)s向后移一位字符,然后再与剩下的p做匹配;

       (2)s向后移一位字符,p向后移一位,然后s和p再进行匹配;

         只要(1)和(2)有一种情况匹配,就算成功了。

        代码如下:

/**
     * @param s: A string 
     * @param p: A string includes "?" or "*"
     * @return: A boolean
     */
    public static boolean isMatch(String s, String p) {
    	if(s == null || p == null){
    		return false;
    	}
    	return isMatch(s.toCharArray(),p.toCharArray(),0,0);
     }
    
    private static boolean isMatch(char[] cs,char[] ps,int csIndex,int psIndex){
    	//如果p匹配到结尾了
    	if(psIndex == ps.length){
    		return csIndex == cs.length;
    	}
    	//如果匹配字符比原字符长,那么返回false
    	if(cs.length - csIndex < ps.length - psIndex){
    		return false;
    	}
    	//如果p只剩一个字符了
    	if(psIndex == ps.length - 1){
    		if(ps[psIndex] == '*' || (csIndex == cs.length - 1 && (ps[psIndex] == '?' || ps[psIndex] == cs[csIndex]))){
    			return true;
    		}
    		return false;
    	}
    	//如果c只剩一个字符了
    	if(csIndex == cs.length - 1){
    		if(psIndex != ps.length - 1){
    			return false;
    		}
    		if(ps[psIndex] == '?' || ps[psIndex] == '*' || ps[psIndex] == cs[csIndex]){
    			return true;
    		}
    		return false;
    	}
    	//如果P还有字符
    	if(ps[psIndex] == cs[csIndex] || ps[psIndex] == '?'){
    		return isMatch(cs,ps,csIndex + 1,psIndex + 1);
    	}else if(ps[psIndex] != '*'){
//    		cache.get(csIndex).set(psIndex,0);
    		return false;
    	}else{
    		//两种情况,p向后移动一位,或者不移动,两种情况有一种匹配到了就可以
    		//p应该向后移动多少位(为了去掉多个*在一块的情况)
    		int pIndex = psIndex;
    		while(ps[pIndex] == '*' && pIndex < ps.length){
    			pIndex++;
    		}
    		return isMatch(cs,ps,csIndex + 1,psIndex) || isMatch(cs,ps,csIndex + 1, pIndex);
    	}
     
    }

         这种方法肯定是没问题的,但是在提交的最后超时了,超时参数为:

"abbabaaabbabbaababbabbbbbabbbabbbabaaaaababababbbabababaabbababaabbbbbbaaaabababbbaabbbbaabbbbababababbaabbaababaabbbababababbbbaaabbbbbabaaaabbababbbbaababaabbababbbbbababbbabaaaaaaaabbbbbaabaaababaaaabb", "**aa*****ba*a*bb**aa*ab****a*aaaaaa***a*aaaa**bbabb*b*b**aaaaaaaaa*a********ba*bbb***a*ba*bb*bb**a*b*bb"

      在我的电脑上执行时间是1359ms

       这种方法不行。这时候就想到了:用递归能完成的代码,大多数都能用动态规划完成。动态规划应该要比递归效率高点。  

       前面做过很多动态规划的例子,有经验了,还是一步一步来:

      (1) 找出题目要求的状态表示。题目要求出字符串s和匹配串p是否匹配成功。既然要分成小问题,那么s和p肯定要分成字符数组,分别比较字符(递归也是这么做的);所以状态表示可以想到:两位数组result[][],其中[i][j]表示s的前i个字符和p的前[j]个字符是否匹配成功。

      (2)找出状态转换方程,就是说找出当result[i][j]和result[i-1][j - x](其中x表示正整数或0,表示p的前j个字符)的关系。很容易想到,这取决于s的第i个字符和p的第j个字符的关系。

      (3)初始化状态数组。应该初始化状态数组result,初始化所有的result[0][j]和result[i][0];

         代码:

 /**  
    * dpMatch: 动态规划解决匹配字符串的问题
    * @param s
    * @param p
    * @return 
    * boolean  返回类型   
    */
    private static boolean dpMatch(String s,String p){
    	if(s == null || p == null){
    		return false;
    	}
    	//单独处理字符s为空的情况
    	if(s.length() == 0){
    		if(p.length() == 0){
    			return true;
    		}
    		int j = 0;
    		while(j < p.length() && p.charAt(j) == '*'){
    			j++;
    		}
    		return j == p.length();
    	}
    	//数组,保存动态规划的结果
    	boolean[][] result = new boolean[s.length()][p.length()];
    	//首先初始化结果数组
    	init(result, s, p);
    	for(int i = 1; i < s.length(); i++){
    		for(int j = 1; j < p.length(); j++){
    			if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '?'){
    				result[i][j] = result[i - 1][j - 1];
    			}else if(p.charAt(j) == '*'){
    				result[i][j] = result[i - 1][j] || result[i][j - 1];
    			}
    		}
    	}
    	return result[s.length() - 1][p.length() - 1];
    }
    
    /**  
    * init: 初始化数组
    * @param result
    * @param s
    * @param p 
    * void  返回类型   
    */
    private static void init(boolean[][] result,String s,String p){
    	//首先初始化第一个匹配传和第一个字符串的匹配情况
    	if(s.charAt(0) == p.charAt(0) || p.charAt(0) == '?' || p.charAt(0) == '*'){
    		result[0][0] = true;
    	}
    	//初始化第一个字符串中字符和前j个匹配串的匹配情况
    	for(int j = 1; j < p.length(); j++){
    		if(p.charAt(j) != '*'){
    			break;
    		}
    		result[0][j] = true;
    	}
    	//初始化第一个匹配串中字符和前i个字符串中字符的匹配情况
    	if(p.charAt(0) == '*'){
    		for(int i = 1; i < s.length(); i++){
    			result[i][0] = true;
        	}
		}
    }

 测试:

long start = System.currentTimeMillis();
		System.out.println(dpMatch("abbabaaabbabbaababbabbbbbabbbabbbabaaaaababababbbabababaabbababaabbbbbbaaaabababbbaabbbbaabbbbababababbaabbaababaabbbababababbbbaaabbbbbabaaaabbababbbbaababaabbababbbbbababbbabaaaaaaaabbbbbaabaaababaaaabb", "**aa*****ba*a*bb**aa*ab****a*aaaaaa***a*aaaa**bbabb*b*b**aaaaaaaaa*a********ba*bbb***a*ba*bb*bb**a*b*bb"));

		long end = System.currentTimeMillis();
		System.out.println(end - start);

 结果:

false
5

 比递归快的不止一点点。。。

猜你喜欢

转载自709002341.iteye.com/blog/2272577
今日推荐