数据结构与算法-----12.字符串匹配:

在软件工程中,我们用到字符串匹配的地方非常多,比如:文本编辑软件中的查找功能,判断两个字符串是否相等。字符串匹配分为两种情况:(1)字符串一对一的匹配,(2)在一个字符串中同时查找多个子串。

1.对于一对一的匹配,有经典的BF算法(Brute Force)暴力匹配算法:

核心思想:字符串匹配算法中有两个核心词:(1)基础字符串(主串)(2)模式串

(例如:在字符串A中查找字符串B,那么A就是主串,B就是模式串)

假如主串长度为m,模式串长度为n,根据模式串的长度,可以将主串分解成m-n+1个子串。然后拿着模式串与主串逐一进行匹配,时间复杂度为O(m*n)

主串:abcbdef     子串:bcd 

可以将主串分解为:abc   bcd   cbd  bde   def 

public class StringMatch {

    public static void main(String[] args) {
        String basic = "zhangsanlisi";
        String pattern = "lisi";
        StringMatch match = new StringMatch();
        int i = match.bf(basic,pattern);
        System.out.println("在基础字符串中第一次出现的位置:"+i);
    }
    /*暴力匹配算法:
    * 将基础字符串按照模式串分解成a-b+1个,然后逐个与模式串进行匹配
    * 时间复杂度为O(n*m) n为基础字符串的长度,m为模式串的长度*/
    public int bf(String basic,String pattern) {
        int a = basic.length();
        int b = pattern.length();
        int k;
        char[] bas = basic.toCharArray();
        char[] pat = pattern.toCharArray();
        /*判断参数是否有效*/
        if(a == 0 || b == 0 || a-b<0) return -1;
        /*当前for循环的意思是:基础串与模式串比较的次数,一个基础串可以分解成a-b+1个模式串*/
        for(int i=0;i<=a-b;i++) {
            k = 0;
            /*拿模式串与当前分解的基础串进行逐个字符的匹配,参数k用来记录匹配到的字符串的个数*/
            for(int j=0;j<b;j++) {
                if(bas[i+j] == pat[j]) {
                    k++;
                }else {
                    break;
                }
            }
           /*如果k与模式串的长度b相等的话,那么证明两个字符串就是相等的*/
            if(k == b) return i;
        }
        return -2;
    }
}

在实际的软件开发中,主串和模式串的长度可能相对比较短,并且在匹配的过程中,如果遇到不相等的字符,就会停止当前的匹配操作,最坏的情况下时间复杂度为O(m*n),但是实际情况都比这个值低,并且暴力匹配算法思想简单,容易实现,所以在条件允许的情况下是首选。

2.Trie树(字典树):

像是Google,百度这样的搜索引擎,当我们在搜索栏中键入某一些文字的时候,它就会在下拉栏中自动提示出一些相应的关键词。这种搜索引擎的自动提示功能就可以使用Trie树来实现。

Trie数的本质就是利用字符串之间的公共前缀,将重复的前缀合并在一起,构成一个(字符串集合)一颗字典树。

Trie树的结构大致如下:

在Trie数中,根节点不包含任何信息,其它每一个节点都包含某一字符串中的一个字符。在Trie中有两个重要的操作:

(1)通过字符串集合来构建一颗Trie树:因为构建Trie树需要遍历所有的字符串,所以时间复杂度为O(n)

(2)在Trie中查询某一个字符串:假设需要查询的字符串长度为k,那么我们只需比对大约k个节点,所以查找的时间复杂度为 O(k)。

3.Trie树和散列表,红黑树的比较:

字符串的匹配问题,归根结底就是数据的查找问题,对于动态高效数据操作的数据结构,其实也可以实现字符串的匹配功能。例如,散列表,红黑树,跳表等。他们各自的优缺点如下:

利用Trie树进行字符串匹配,其实对数据的要求比较苛刻:

(1)首先字符集不能太大,因为Trie树在存储数据时,不仅要存储数据本身,还要存储指针的引用,所以会浪费更多的空间。

(2)同时需要字符串前缀重合的比较多,不然同样会消耗更多的空间。

(3)Tried树中的数据是通过指针串起来的,所以在内存中的分布是不连续的,对CPU缓存不友好。

(4)如果要使用Trie树来实现字符串匹配,需要从0到1来构建,增加了工程的复杂度。

实际上,Tried树只是不适合精确查找,如果进行精确查找,那么可以使用其它动态操作数据的容器。Trie树适合匹配查找这种应用场景

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章