浅谈字符串模式匹配
0 緒論
字符串模式匹配是計(jì)算機(jī)科學(xué)中最古老、研究最廣泛的問題之一(其實(shí)也就幾十年)。字符串匹配問題就是在一個(gè)字符串T中搜索某個(gè)字符串P的所有出現(xiàn)位置。其中,T稱為文本,P稱為模式,T和P都定義在同一個(gè)字母表∑上。簡單來說,就是在一個(gè)目標(biāo)串T中看有沒有子串與另一個(gè)較短的字符串相同。
字符串串匹配算法雖然發(fā)展了幾十年,然而非常實(shí)用的算法是近年才出現(xiàn)。
傳統(tǒng)的串匹配算法可以概括為前綴搜索、后綴搜索、子串搜索。比如以KMP,Shift-And,Shift-Or,BM等子串搜索,以后綴自動機(jī)為代表的的后綴搜索等。
近些年則流行以哈希函數(shù)為基礎(chǔ)的哈希匹配。
字符串模式匹配的應(yīng)用包括生物信息學(xué)、信息檢索、拼寫檢查、語言翻譯、數(shù)據(jù)壓縮、網(wǎng)絡(luò)入侵檢測等方面。(你見到最多的應(yīng)用應(yīng)該是論文查重)
字符串匹配工作一直是IDS研究領(lǐng)域中的熱點(diǎn)之一。在網(wǎng)絡(luò)速度迅速發(fā)展的今天,基于字符匹配技術(shù)的網(wǎng)絡(luò)應(yīng)用存在著性能瓶頸,提高系統(tǒng)的整體性能是研究和設(shè)計(jì)者的主要工作。字符匹配是其實(shí)現(xiàn)網(wǎng)絡(luò)入侵檢測的主要技術(shù)之一,因此,高效的算法將是提高系統(tǒng)總體性能的手段之一。
這篇博客將簡要介紹BF算法(暴力算法),KMP算法和簡單的hash匹配。
1 BF算法
(1)簡介
字符串的暴力匹配算法也被稱為Brute-Force算法(其實(shí)就是“暴力”的英語)簡稱BF算法。
(2)算法描述
采用窮舉的方法,將文本T字符串的第一個(gè)字符與模式P字符串的第一個(gè)字符比較,若相等則逐個(gè)比較后續(xù)字符,否則將文本T字符串的第二個(gè)字符與模式P字符串的第一個(gè)字符比較。
(3)算法分析
假設(shè)文本T的字符串長度為n,模式P的字符串長度為m,則BF算法的最好時(shí)間復(fù)雜度為O(n),最壞時(shí)間復(fù)雜度為O(n*m),平均時(shí)間復(fù)雜度O(n*m),空間復(fù)雜度為O(n+m)。
(4)算法實(shí)現(xiàn)
#include<iostream>
#include<string>
#include<bits/stdc++.h>
using namespace std;
int BF(string t,string p)//返回t與p匹配成功的次數(shù)
{
int num=0;
for(int i=0;i<t.length();i++)
{
int j;
for(j=0;j<p.length();j++)
if(t[i+j]!=p[j])break;
if(j==p.length())
{
num++;
cout<<"匹配成功位置:"<<i<<endl;
}
}
return num;
}
int main()
{
string t,p;
cin>>t>>p;
cout<<BF(t,p)<<endl;
return 0;
}
2 KMP算法
(1)簡介
KMP算法是由D.E.Kruth,J.h.Morris和V.R.Pratt共同提出,因此人們稱它為克努特—莫里斯—普拉特操作,簡稱KMP算法。該算法是大多數(shù)初學(xué)者學(xué)習(xí)字符串匹配的第一個(gè)高效算法,也是非暴力算法中比較好理解的一個(gè)。
(2)算法描述
KMP算法定義了一個(gè)next數(shù)組,通過next數(shù)組在發(fā)現(xiàn)匹配中途出現(xiàn)匹配失效時(shí)減少回溯長度來提高匹配效率。
next數(shù)組可以理解為:next[j](假設(shè)next[j]=k)表示模式p中p[j-k],p[j-k+1]···p[j-1]與p[0],p[1]···p[k-1]相同。在模式匹配的過程中,當(dāng)匹配在p[j]失效時(shí),指向p的s指針回退到next[j],而非完全回溯。
KMP還有一種改進(jìn)算法:由之前的定義可得next[j]=k且p[j]=p[k]時(shí),若t[i]
epp[j]就必有t[i]
epp[k]。此時(shí)沒有必要再將t[i]與p[k]比較,可以直接將t[i]與p[next[k]]比較。
(3)算法分析
假設(shè)文本T的字符串長度為n,模式P的字符串長度為m,則KMP算法的最好時(shí)間復(fù)雜度為O(n+m),最壞時(shí)間復(fù)雜度為O(n*m),平均時(shí)間復(fù)雜度O(n+m),空間復(fù)雜度為O(n+m)。
(4)算法實(shí)現(xiàn)
以下為改進(jìn)的KMP算法的實(shí)現(xiàn):
#include<iostream>
#include<bits/stdc++.h>
#include<string>
using namespace std;
int next[1000];
void get_next(string p)//計(jì)算next數(shù)組
{
int len=p.length();
int j=0,k=-1;
next[0]=-1;
while(j<len)
{
if(k==-1||p[j]==p[k])
{
j++;k++;
if(p[j]==p[k])next[j]=next[k];
else next[j]=k;
}
else k=next[k];
}
}
int KMP(string t,string p)//進(jìn)行正式匹配并返回匹配成功的次數(shù)
{
int tlen=t.length(),plen=p.length();
int i=0,j=0,num=0;
while(i<tlen)
{
if(j==-1||t[i]==p[j])
{
if(j==plen-1)
{
num++;
cout<<":"<<i-j+1<<'
';
j=next[j];
}
else {i++;j++;}
}
else j=next[j];
}
return num;
}
int main()
{
string t,p;
cin>>t>>p;
get_next(p);
int num=KMP(t,p);
cout<<num<<endl;
return 0;
}
3 hash算法
(1)簡介
hash匹配算法是將hash算法運(yùn)用到字符串匹配中的一個(gè)例子,并被廣泛運(yùn)用。hash匹配算法本生就具有多種,在此僅介紹簡單的hash匹配算法。
(2)算法描述
普通hash匹配即每次截取模式串長度的字串計(jì)算hash值,然后原模式串的hash值進(jìn)行比較,兩者相同則認(rèn)為匹配成功。
hash值的一個(gè)簡單計(jì)算方法為
[hash(string)=(sum_{i=1}^nk*d^{i-1})mod(p)
]
其中n表示字符串的長度,k表示字符串的第i個(gè)字符在字符集中的序號,d表示hash基數(shù)一般取大質(zhì)數(shù)(大于字符集的寬度),p表示模運(yùn)算底數(shù),一般取232或者264。
滾動hash匹配是普通hash匹配的優(yōu)化版,優(yōu)化之處在于不再頻繁計(jì)算原串子串的hash值,而是通過前一個(gè)子串的hash值得到后一個(gè)子串的hash值。
(3)算法分析
假設(shè)文本T的字符串長度為n,模式P的字符串長度為m,則普通hash算法的最好時(shí)間復(fù)雜度為O(nm),最壞時(shí)間復(fù)雜度為O(nm),平均時(shí)間復(fù)雜度O(n^m),空間復(fù)雜度為O(n+m)。滾動hash算法的最好時(shí)間復(fù)雜度為O(n+m),最壞時(shí)間復(fù)雜度為O(n+m),平均時(shí)間復(fù)雜度O(n+m),空間復(fù)雜度為O(n+m)。
(4)算法實(shí)現(xiàn)
以下為滾動hash算法的實(shí)現(xiàn)
#include<iostream>
#include<string>
using namespace std;
const unsigned int size=233;
int hash(string t,string p)//進(jìn)行hash匹配
{
int num=0;
int tlen=t.length(),plen=p.length();
unsigned int k=1,temph=0,ph=0;
for(int i=0;i<plen;i++)
{
ph=ph*size+p[i];
temph=temph*size+t[i];
}
for(int i=1;i<plen;i++)k*=size;
if(ph==temph)
{
num++;
cout<<"1
";
}
for(int i=plen;i<tlen;i++)
{
temph-=k*t[i-plen];
temph=temph*size+t[i];//滾動計(jì)算hash值
if(temph==ph)
{
num++;
cout<<i-plen+2<<'
';
}
}
return num;
}
int main()
{
string t,p;
cin>>t>>p;
int num=hash(t,p);
cout<<num<<endl;
return 0;
}
4 總結(jié)
本文簡要介紹了面向初學(xué)者的關(guān)于字符串匹配的基礎(chǔ)知識,希望讀者能有所收獲。
總結(jié)
- 上一篇: 什么一键重装系统最好(一键重装软件哪个干
- 下一篇: Maven安装配置教程