關於字串的匹配之古典匹配到引入KMP演算法
最近在學習資料結構,學到了串與字串那個地方,一般都是求子串在主串中的定位問題:
古典的方法一般都是採用子串定長順序儲存結構,可以寫出不依賴於其他串的操作的匹配演算法,這種演算法在二進位制中的算術複雜度非常高,最壞情況下為O(n*m),但是我個人覺得在不是二進位制中應該也還好,這個演算法的核心實際上就是將子串固定死,然後一個下標i在主串中移動,或者就是利用一下i=i-j+2,然後J又回到等於1,然後如果說最後j能夠大於子串的長度,那麼i減掉子串的長度就是子串此時在主串中的索引;
下面可以給出他的這個函式演算法:
#include<cstdio> #include<iostream> #include<cstring> #include<string> #include<stack> #include<cstdlib> #include<algorithm> #include<sstream> using namespace std; void Index(string s1,string s2,int pos) { //1=<pos<=strlen(s1); int i = pos; int j = 1; while(i <= s1.length() && j <= s2.length()) { if(s1[i] == s2[j]) { ++i; ++j; } else { i = i - j + 2; j = 1; } } if(j > s2.length()) { int value = i - s2.length(); printf("第%d個位置一致:",value); } else { return; } } int main() { string s1,s2; printf("請輸入兩個字串分別為主串和模式子串:"); cin >> s1 >> s2; Index(s1,s2,1); return 0; }
而這個時候我們可以引進來KMP演算法來優化解題:
KMP演算法實際上是由get_next(string s2,int next[])和Index_KMP(string s1,string s2,int pos)兩個函式組成,
這個next函式實際上是對模式串進行操作,跟主串完全沒有關係的,在next[1]=0,next[2]=1,這兩個實際上就是固定的,我們這樣在紙上是可以直接列出來的,但是怎麼寫出自動判斷next【j】的值呢:
void get_next(string s3,int next[]) { //求模式串T的next函式值並且存入陣列next int i = 1; next[1] = 0; next[2] = 1; int j = 0; while(i < s3.length()) { if(j == 0 || s3[i] == s3[j]) { ++i; ++j; next[i] = j; } else { j = next[j]; } } } //get_next
然後下面給出Index_KMP演算法函式:
int Index_KMP(string s1,string s2,int pos) { int i = pos; int j = 1; while(i <= s1.length() && j <= s2.length()) { if(j == 0 || s1[i] == s2[j]) { i++; j++; } else { j = get_next[j]; } if(j > s2.length()) { return i - s2.length(); } } }
下面再以一道題目來解釋吧:
串結構練習——字串匹配
Time Limit: 1000ms Memory limit: 65536K 有疑問?點這裡^_^
題目連結:http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2125
題目描述
給定兩個字串string1和string2,判斷string2是否為string1的子串。
輸入
輸入包含多組資料,每組測試資料包含兩行,第一行代表string1,第二行代表string2,string1和string2中保證不出現空格。
輸出
對於每組輸入資料,若string2是string1的子串,則輸出"YES",否則輸出"NO"。
示例輸入
abc a 123456 45 abc ddd
示例輸出
YES YES NO
程式碼如下所示:
#include<string.h>
#include<iostream>
using namespace std;
char S[100],T[100];
int s,t,next[100],nextval[100];
void get_next(char T[],int next[]);//對應模式匹配演算法一next函式值
void get_nextval(char T[],int nextval[]);//對應模式匹配演算法二nextval函式值
int Index_KMP(char S[],char T[],int pos);//模式匹配演算法一
int Index_KMP2(char S[],char T[],int pos);//模式匹配演算法二
int main()
{
while(cin>>S>>T)
{
memset(next,0,sizeof(next));
int i;
s=strlen(S);
t=strlen(T);
for(i=s;i>=1;i--)
S[i]=S[i-1];
S[s+1]='\0';
for(i=t;i>=1;i--)
T[i]=T[i-1];
T[t+1]='\0';
//get_next(T,next);
get_nextval(T,nextval);
//int flag=Index_KMP(S,T,1);//通過這裡可以驗證兩個演算法
int flag=Index_KMP2(S,T,1);
//for(i=1;i<=t;i++)
// cout<<nextval[i]<<" ";
//cout<<endl;
if(flag==0)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
void get_next(char T[],int next[])//對應模式匹配演算法一next函式值
{
int i=1,j=0;
next[1]=0;
while(i<t)
{
if(j==0||T[i]==T[j])
{
++i;
++j;
next[i]=j;
}
else j=next[j];
}
}
void get_nextval(char T[],int nextval[])//對應模式匹配演算法二nextval函式值
{
int i=1,j=0;
nextval[1]=0;
while(i<t)
{
if(j==0||T[i]==T[j])
{
++i;
++j;
if(T[i]!=T[j])
nextval[i]=j;
else
nextval[i]=nextval[j];
}
else
j=nextval[j];
}
}
int Index_KMP(char S[],char T[],int pos)//模式匹配演算法一
{
int i=pos,j=1;
while(i<=s&&j<=t)
{
if(j==0||S[i]==T[j])
{
++i;
++j;
}
else j=next[j];
}
if(j>t)return i-t;
else return 0;
}
int Index_KMP2(char S[],char T[],int pos)//模式匹配演算法二
{
int i=pos,j=1;
while(i<=s&&j<=t)
{
if(j==0||S[i]==T[j])
{
++i;
++j;
}
else j=nextval[j];
}
if(j>t)return i-t;
else return 0;
}
還有一道題目是南陽OJ上面的,題目的基本意思是問你在主串中,子串出現的次數:
下面是程式碼:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<stack>
#include<cstdlib>
using namespace std;
int main() {
int n; //表示n組測試資料
// cin >> n;
string s1; //字串s1比較短
string s2;
int cnt = 0;
int len1 = s1.length();
int len2 = s2.length();
cin >> n;
while(n--) {
cnt = 0;
cin >> s1;
cin >> s2;
int ans = s2.find(s1); //在s2子串裡面查詢s1
while(1) { //用了一個死迴圈加break;
if(s2.find(s1) != -1) {
cnt++;
s2[s2.find(s1)] = '3'; //這一部是關鍵,相當於把走過的路都做了標記
} else {
break;
}
}
cout << cnt << endl;
}
}
下面的是OJ上看到的參考程式碼:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1,s2;
int n;
cin>>n;
while(n--)
{
cin>>s1>>s2;
unsigned int m=s2.find(s1,0);
int num=0;
while(m!=string::npos)
{
num++;
m=s2.find(s1,m+1);
}
cout<<num<<endl;
}
}