日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

字符串匹配算法_多字符串匹配

發布時間:2023/12/19 综合教程 40 生活家
生活随笔 收集整理的這篇文章主要介紹了 字符串匹配算法_多字符串匹配 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. BM(Boyer-Moore)算法

  • 思想:有模式串中不存在的字符,那么肯定不匹配,往后多移動幾位,提高效率
  • BM原理:壞字符規則,好后綴規則

1.1 壞字符規則

  • 利用壞字符規則,BM算法在最好情況下的時間復雜度非常低,是O(n/m)。比如,主串是aaabaaabaaabaaab,模式串是aaaa。每次比對,模式串都可以直接后移四位,所以,匹配具有類似特點的模式串和主串的時候,BM算法非常高效。
  • 單純使用壞字符規則還是不夠的。因為根據 si-xi計算出來的移動位數有可能是負數,比如主串是aaaaaaaaaaaaaaaa,模式串是baaa。不但不會向后滑動模式串,還有可能倒退。所以,BM算法還需要用到“好后綴規則”。

1.2 好后綴規則


從好后綴的后綴子串中,找一個最長的且和模式串的前綴子串匹配的 {v},滑動至 {v} 對齊

1.3 兩種規則如何選擇

  • 分別計算好后綴和壞字符規則往后滑動的位數,取大的,作為滑動位數(還可以避免負數)

2. BM算法代碼實現

2.1 壞字符

  • 找到壞字符在模式串中的位置(有重復的,則是靠后的那個)
    采用哈希,而不是遍歷。
#define SIZE 256 //字符集字符數
void generateBadChar(char *b, int m, int *badchar)//(模式串字符b,模式串長度m,模式串的哈希表)
{ 
   
    int i, ascii;
    for(i = 0; i < SIZE; ++i)
    { 
   
        badchar[i] = -1;//哈希表初始化為-1
    }
    for(i = 0; i < m; ++i)
    { 
   
        ascii = int(b[i]);  //計算字符的ASCII值
        badchar[ascii] = i;//重復字符被覆蓋,記錄的是最后出現的該字符的位置
    }
}

int str_bm(char *a, int n, char *b, int m)
//只考慮壞字符方法的程序框架
{ 
   
    int *badchar = new int [SIZE];//記錄模式串中每個字符最后出現的位置
    generateBadChar(b,m,hash);     //構建壞字符哈希表
    int i = 0, j;
    while(i < n-m+1)
    { 
   
        for(j = m -1; j >= 0; --j)  //模式串從后往前匹配
        { 
   
            if(a[i+j] != b[j])
                break;  //壞字符對應模式串中的下標是j
        }
        if(j < 0)   //匹配成功
        { 
   
            return i;   //返回主串與模式串第一個匹配的字符的位置
        }
        //這里等同于將模式串往后滑動 j-badchar[int(a[i+j])] 位
        i = i + (j - badchar[int(a[i+j])]);
    }
    return -1;
}

2.2 好后綴

  • 在模式串中,查找跟好后綴匹配的另一個子串
  • 在好后綴的后綴子串中,查找最長的、能跟模式串前綴子串匹配的后綴子串

不考慮效率的話,上面兩個操作都可以暴力查找;
解決辦法: 預先對模式串進行處理。


實現過程:

預處理模式串,填充suffix,prefix

void generateGS(char *b, int m, int *suffix, bool *prefix)
//預處理模式串,填充suffix,prefix
{ 
   
    int i, j, k;
    for(i = 0; i < m; ++i)//兩個數組初始化
    { 
   
        suffix[i] = -1;
        prefix[i] = false;
    }
    for(i = 0; i < m-1; ++i)//b[0,i]
    { 
   
        j = i;
        k = 0;//公共后綴子串長度(模式串尾部取k個出來,分別比較)
        while(j >= 0 && b[j] == b[m-1-k])//與b[0,m-1]求公共后綴子串
        { 
   
            --j;
            ++k;
            suffix[k] = j+1;
            //相同后綴子串長度為k時,該子串在b[0,i]中的起始下標
            // (如果有多個相同長度的子串,被賦值覆蓋,存較大的)
        }
        if(j == -1)//查找到模式串的頭部了
            prefix[k] = true;//如果公共后綴子串也是模式串的前綴子串
    }
}

計算滑動位數

  • case1:
  • case2:
  • case3:(以上都不成立,移動整個模式串(長度m))

2.3 完整代碼

/** * @description: 字符匹配BM算法 * @author: michael ming * @date: 2019/6/18 22:19 * @modified by: */
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;
#define SIZE 256 //字符集字符數
void generateBadChar(char *b, int m, int *badchar)//(模式串字符b,模式串長度m,模式串的哈希表)
{ 

int i, ascii;
for(i = 0; i < SIZE; ++i)
{ 

badchar[i] = -1;//哈希表初始化為-1
}
for(i = 0; i < m; ++i)
{ 

ascii = int(b[i]);  //計算字符的ASCII值
badchar[ascii] = i;//重復字符被覆蓋,記錄的是最后出現的該字符的位置
}
}
void generateGS(char *b, int m, int *suffix, bool *prefix)//預處理模式串,填充suffix,prefix
{ 

int i, j, k;
for(i = 0; i < m; ++i)//兩個數組初始化
{ 

suffix[i] = -1;
prefix[i] = false;
}
for(i = 0; i < m-1; ++i)//b[0,i]
{ 

j = i;
k = 0;//公共后綴子串長度(模式串尾部取k個出來,分別比較)
while(j >= 0 && b[j] == b[m-1-k])//與b[0,m-1]求公共后綴子串
{ 

--j;
++k;
suffix[k] = j+1;
//相同后綴子串長度為k時,該子串在b[0,i]中的起始下標
// (如果有多個相同長度的子串,被賦值覆蓋,存較大的)
}
if(j == -1)//查找到模式串的頭部了
prefix[k] = true;//如果公共后綴子串也是模式串的前綴子串
}
}
int moveByGS(int j, int m, int *suffix, bool *prefix)//傳入的j是壞字符對應的模式串中的字符下標
{ 

int k = m - 1 - j;//好后綴長度
if(suffix[k] != -1)//case1,找到跟好后綴一樣的模式子串(多個的話,存的靠后的那個(子串起始下標))
return j - suffix[k] + 1;
for(int r = j + 2; r < m; ++r)//case2
{ 

if(prefix[m-r] == true)//m-r是好后綴的子串的長度,如果這個好后綴的子串是模式串的前綴子串
return r;//在上面沒有找到相同的好后綴下,移動r位,對齊前綴到好后綴
}
return m;//case3,都沒有匹配的,移動m位(模式串長度)
}
int str_bm(char *a, int n, char *b, int m)//a表示主串,長n; b表示模式串,長m
{ 

int *badchar = new int [SIZE];//記錄模式串中每個字符最后出現的位置
generateBadChar(b,m,badchar);     //構建壞字符哈希表
int *suffix = new int [m];
bool *prefix = new bool [m];
generateGS(b, m, suffix, prefix);   //預處理模式串,填充suffix,prefix
int i = 0, j, moveLen1, moveLen2;//j表示主串與模式串匹配的第一個字符
while(i < n-m+1)
{ 

for(j = m -1; j >= 0; --j)  //模式串從后往前匹配
{ 

if(a[i+j] != b[j])
break;  //壞字符對應模式串中的下標是j
}
if(j < 0)   //匹配成功
{ 

delete [] badchar;
delete [] suffix;
delete [] prefix;
return i;   //返回主串與模式串第一個匹配的字符的位置
}
//這里等同于將模式串往后滑動 j-badchar[int(a[i+j])] 位
moveLen1 = j - badchar[int(a[i+j])];//按照壞字符規則移動距離
moveLen2 = 0;
if(j < m-1)//如果有好后綴的話
{ 

moveLen2 = moveByGS(j,m,suffix,prefix);//按照好后綴規則移動距離
}
i = i + max(moveLen1,moveLen2);//取大的移動
}
delete [] badchar;
delete [] suffix;
delete [] prefix;
return -1;
}
int main()
{ 

string a = "abcacabcbcbacabc", b = "cbacabc";
cout << a << "中第一次出現" << b << "的位置(從0開始)是:" << str_bm(&a[0],a.size(),&b[0],b.size());
return 0;
}

2.4 調試

為方便調試,將字符集SIZE改為3,ascii = int(b[i]-'a')

  • 壞字符在模式串中的位置(靠后的那個)
    badchar[0]:a是4
    badchar[1]:b是5
    badchar[2]:c是6
  • 預處理模式串
  • 按規則移動



3. 總結

  • BM算法的內存消耗
    整個算法用到了額外的3個數組,其中bc數組的大小跟字符集大小有關,suffix數組和prefix數組的大小跟模式串長度m有關。
    如果處理字符集很大的字符串匹配問題,badchar數組對內存的消耗就會比較多。
    因為好后綴壞字符規則是獨立的,如果運行的環境對內存要求苛刻,可以只使用好后綴規則,不使用壞字符規則,就可以避免badchar數組過多的內存消耗。不過,單純使用好后綴規則的BM算法效率就會下降一些了。
  • 時間復雜度
    以上BM算法是個初級版本。這個版本,在極端情況下,預處理計算suffix數組、prefix數組的性能會比較差。
    比如模式串是aaaaaaa這種包含很多重復的字符的模式串,預處理的時間復雜度就是O(m^2)。如何優化這種極端情況下的時間復雜度退化,以后再找空研究。
    實際上,BM算法的時間復雜度分析起來是非常復雜,論文“A new proof of the linearity of the Boyer-Moore string searching algorithm”證明了在最壞情況下,BM算法的比較次數上限是5n。論文“Tight bounds on the complexity of the Boyer-
    Moore string matching algorithm”證明了在最壞情況下,BM算法的比較次數上限是3n。

  • BM算法核心思想是,利用模式串本身的特點,在模式串中某個字符與主串不能匹配的時候,將模式串往后多滑動幾位,以此來減少不必要的字符比較提高匹配的效率
  • BM算法構建的規則有兩類,壞字符規則和好后綴規則。
  • 好后綴規則可以獨立于壞字符規則使用。
  • 因為壞字符規則的實現比較耗內存,為了節省內存,我們可以只用好后綴規則來實現BM算法。

總結

以上是生活随笔為你收集整理的字符串匹配算法_多字符串匹配的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。