• 数据结构>串的模式匹配算法


    字符串模式匹配有着广泛的应用,如求最大公共子串、最长回文字符串、L-Gap、数据压缩、DNA序列匹配等问题。所谓模式匹配就是在目标字符串中寻找第一个子串在目标串中的位置的过程,要寻找的字串即为模式。

            目标 T :Beijing

            模式 P :jin

            匹配结果= 3  

    一、BF算法(也叫传统的字符串模式匹配算法、朴素模式匹配、穷举模式匹配)

    BF(Bruce Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符;若不相等,则比较S的第二个字符和P的第一个字符,依次比较下去,直到得出最后的匹配结果。(通俗的解释)

    BF(Bruce Force)算法可以说是模式匹配算法中最简单、最容易理解的一个。原理很简单。其基本思想是从主串的start位置开始与模式串进行匹配,如果相等,则继续比较后续字符,如果不相等则模式串回溯到开始位置,主串回溯到start+1位置,继续进行比较直至模式串的所有字符都已比较成功则匹配成功,或者主串所有的字符已经比较完毕,没有找到完全匹配的字串,则匹配失败。(引用别人的解释)

    1.1举例说明

        S:  ababcababa

        P:  ababa

    BF算法匹配的步骤如下

                i=0                                   i=1                                 i=2                               i=3                              i=4

      第一趟:ababcababa         第二趟:ababcababa      第三趟:ababcababa    第四趟:ababcababa    第五趟:ababcababa

                  ababa                              ababa                            ababa                          ababa                          ababa

                   j=0                                    j=1                                 j=2                              j=3                              j=4(i和j回溯)

                  i=1                                   i=2                                  i=3                                i=4                         i=3

    第六趟:ababcababa         第七趟:ababcababa       第八趟:ababcababa     第九趟:ababcababa   第十趟:ababcababa

                 ababa                                ababa                             ababa                          ababa                          ababa

                 j=0                                        j=0                                         j=1                             j=2(i和j回溯)               j=0

                          i=4                                    i=5                                 i=6                                 i=7                                 i=8

    第十一趟:ababcababa       第十二趟:ababcababa    第十三趟:ababcababa   第十四趟:ababcababa   第十五趟:ababcababa

                           ababa                                  ababa                              ababa                            ababa                            ababa

                           j=0                                        j=0                                     j=1                                   j=2                                   j=3

                                   i=9

    第十六趟:ababcababa

                             ababa

                                     j=4(匹配成功)

    1.2参考别人的C代码实现

    public class BR_String {
    
    	/**
    	 * 
    	 * @param s
    	 *            主串
    	 * @param p
    	 *            模式串
    	 * @return 模式串在主串中第一次出现的位置,如果找不到则返回-1
    	 */
    	static int BF(String s, String p) {
    		int i = 0, j = 0;
    
    		while (i < s.length())
    
    		{
    
    			j = 0;
    
    			while (j < p.length() && s.charAt(i) == p.charAt(j)) // 如果相等,则继续比较后续字符
    
    			{
    
    				i++;
    
    				j++;
    
    			}
    
    			if (j == p.length())
    
    				return i - p.length();
    
    		   i = i - j + 1; // 指针i回溯
    
    		}
    
    		return -1;
    	}
    
    	public static void main(String[] args) {
    		String s = "ababcababa";
    		String p = "ababa";
    		System.out.println(s.indexOf(p));// 用来验证的
    		System.out.println(BF(s, p));
    
    	}
    }
    

    1.3、第一次看完BF算法的定义以后自己写的代码

    以下代码是第一次看完BF算法的定义以后自己写的。和上面基本相同,可能理解起来简单点。以主串为参照,主串的第i个字符和模式串的第一个字符比较,第[i+j]个字符和模式串的第j个字符比较,s[i+j] 和p[j]不相等时候,j变成0,i++,相当于回溯了)

    public class BR_String {
    
    	/**
    	 * 
    	 * @param s
    	 *            主串
    	 * @param p
    	 *            模式串
    	 * @return 模式串在主串中第一次出现的位置,如果找不到则返回-1
    	 */
    	static int BF(String s, String p) {
    		int m = s.length() - p.length(); //
    		int n = p.length();
    		int index = 0; // 记录位置,以便成功时返回字串在主串的位置
    		int start = 0;// start为从第start位置的字符开始比较
    
    		while (start < n && index <= m) {
    
    			if (s.charAt(index + start) == p.charAt(start))
    				// 如果相等,则继续比较后续字符
    				start++;
    
    			else {
    				// 如果不相等则模式串回溯到开始位置,主串回溯到start+1位置,继续进行比较;
    				index++;
    				start = 0;
    			}
    
    		}
    		if (start == p.length()) {
    
    			return index;
    		}
    
    		return -1;
    	}
    
    	public static void main(String[] args) {
    		String s = "ababcababa";
    		String p = "ababa";
    		System.out.println(BF(s, p));
    		System.out.println(s.indexOf(p));// 用来验证的
    
    	}
    }
    

    其实在上面的匹配过程中,有很多比较是多余的。在第五趟匹配失败的时候,在第六趟,i可以保持不变,j值为2。因为在前面匹配的过程中,对于串S,已知s0s1s2s3=p0p1p2p3,又因为p0!=p1!,所以第六趟的匹配是多余的。又由于p0==p2,p1==p3,所以第七趟和第八趟的匹配也是多余的。在KMP算法中就省略了这些多余的匹配。

    二、KMP算法

    一种由Knuth(D.E.Knuth)、Morris(J.H.Morris)和Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法,就取三个人名字的首字母作为该算法的名字可使目标串的检测指针每趟不回退。其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。

    在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[0...j-1]中最长后缀的长度等于相同字符序列的前缀。

    1、失效函数

    定义:若设模式 P =p0p1pm-2pm-1

    示例:确定失效函数f(j)

     

    2、利用失效函数 f (j)的匹配处理

    如果  j =0,则目标指针加 1,模式指针回到p0

    如果 j >0,则目标指针不变,模式指针回到pf(j-1)+1。

    public static int KMPMatch(String s, String p) {
    
    		int next[] = getNext(p);
    		int i = 0, j = 0;
    		while (i < s.length())
    
    		{
    			if ((s.charAt(i) == p.charAt(j))) { // 匹配的时候 ,目标串和模式串的指针都后移
    				i++;
    				j++;
    			}
    
    			else if (j == 0) // 不匹配,如果 j=0,相当于和模式串的第0个字符不相等,则目标指针加
    								// 1,继续和模式串的第0个字符比较。
    				i++;
    
    			else
    				j = next[j - 1] + 1; // 不匹配,如果 j>0,则目标指针不变,模式指针回到 next(j-1)+1。
    
    			if (j == p.length())
    
    				return i - p.length();
    
    		}
    
    		return -1;
    	}
    

    3、失效函数的求法

    1、第一种思路是用递推的思想去求算

    public static int[] getNext(String p) {
    
    		int next[] = new int[p.length()];
    
    		next[0] = -1;
    
    		for (int j = 1; j < p.length(); j++) {
    
    			int i = next[j - 1];
    
    			while (p.charAt(i + 1) != p.charAt(j) && i >= 0) //这里搞不明白
    				i = next[i];
    
    			if (p.charAt(i + 1) == p.charAt(j))   
    				next[j] = i + 1;
    
    			else
    				next[j] = -1;
    		}
    
    		return next;
    	}

    2、根据定义直接求解

    public static int[] getNext_by_Definition(String p) {
    
    		int next[] = new int[p.length()];
    
    		next[0] = -1;
    
    		for (int j = 1; j < p.length(); j++) {
    
    			int i = j;
    
    			while (i <= j && i > 0) {
    				if (p.substring(0, i).equals(p.substring(j - i + 1, j + 1)))
    
    				{
    					next[j] = i - 1;
    					break;
    				}
    
    				else {
    					i--;
    				}
    
    				if (i == 0)
    					next[j] = -1;
    
    			}
    
    		}
    
    		return next;
    	} 

    参考:KMP算法

  • 相关阅读:
    11_listview入门
    10_上午回顾&数据库事务
    09_sqlite两种api比较
    08_通过谷歌封装的api操作数据库update&query
    微软宣布下一代集成开发环境 — Visual Studio 2019
    微软宣布下一代集成开发环境 — Visual Studio 2019
    Chrome 调试技巧
    Chrome 调试技巧
    Chrome 调试技巧
    程序员最喜欢说的十句话,看看你最喜欢说哪句?
  • 原文地址:https://www.cnblogs.com/xqzt/p/5637147.html
Copyright © 2020-2023  润新知