文字列のパターンマッチング - 暴力とKMPアルゴリズム

免責事項:この記事はブロガーのオリジナルの記事、再現され、添付してください明ソース^ _ ^ https://blog.csdn.net/cprimesplus/article/details/90374592

1.BFアルゴリズム(暴力)

初期化の2つの配列添字文字サイクル、インデックスは次成功したマッチまで同時に比較する要素と同じである、2つの文字列が等しい比較し、マッチの等しくない文字列は、インデックス0をフォールバック、整合されます初期位置+1サイクルの添字フォールバック文字列比較。

コードは以下の通りです

#include<iostream>
#include<string>
using namespace std;
int BFMatching(string s1, string s2, int pos)
{
	int len1 = s1.length();
	int len2 = s2.length();
    if(len2 > len1 || !len2) return -1;
	int i = pos, j = 0;
	while(i<len1 && j<len2){
		if(s1[i] == s2[j]){
			j++;i++;
		}
		else{
			i = i-j+1;j = 0;
		}
	}
    if(i<=len1 && j==len2) return i-j+1;
	return -1;
}
int main()
{
	string s1 = "HarryPotter";
	string s2 = "rryP";
	cout<<BFMatching(s1, s2, 0);
	return 0;
} 

本当に良い暴力?(暴力と呼ばれる方法が行くように良いことができます)

私たちは、極端な栗、対象の文字列取ることができ  、「abbababca」 であるためには、文字列のパターンマッチングを  「abababca」パターン文字列は、以下の絵、最初の試合に一致するように望んでいます: 

不幸な事は--iポインタが位置に移動が起こったときに、成功したマッチのすべての6つの要素の現在の顔を見つけることは困難ではない、勝利を手にあり、J Cのポインタは、この時点での位置に移動不一致を遭遇状況。方法は?唯一の元の開始点にアイデアBFアルゴリズム、Jポインタによれば、私は、ポインタを次の位置の最初の開始点から比較しただけにして、はるかに良好ではありません。 

ケースをパターンマッチングの尺度としてJポインタ、ロールバックが完全一致とパターン文字列の添字を見つけるために、すべての後に、理解することができ、前後に比較は避けられないようです。しかし、私は、元の出発点にフォールバック、その後はほぼ限りマッチが失敗したとして、ターゲット文字列の添字としてポインタ、これはそれが少しも過言ではないでしょうか?

だから、私はそれがロールバックしないことができる方法はありますか?

これは、KMPアルゴリズムの導入となります。

 

2.KMPアルゴリズム

また、私は、元の出発点をロールバックしないように設計されたアルゴリズムを、対応する問題を提唱し、非常に悪い状況 - 上記、我々は、極端な環境BFアルゴリズムを議論しました。

、P領域の文字列にグレースケールになり、最初の4つの文字が同じ4つの文字されている。再び図注意深い観察の上に、パターン文字列Sと呼ばれるものが見つけることができ、対象の文字列がPと呼ばれていると仮定すると、 :成功をマッチングSパターン文字列の最初の4つの文字との一部であるABAB、ちょうどいい!

这也就意味着,我们无需将 i 回退到最初的起点。

换言之,S 串标成灰色的前四个字符,恰好是 P 串 标灰部分的后四个字符,而这四个字符因为已经比较过的缘故,无需再进行比较。此时 j 仅仅需要回退到 P[4],而 i 则保留在原来的位置,与 P[4] 之后的元素接着进行比较。

其中黄色区域为接下来待比较的部分。

将上面的做法进行总结,可以归纳为一段话(引自严蔚敏版《数据结构》):每当一趟匹配过程中出现字符比较不相等时,不需要回溯 i 指针,而是利用已经得到的“部分匹配”的结果将模式串向右“滑动”尽可能远的一段距离后,继续进行比较。

上面提到“尽可能远”的距离,也就是尽可能多的和模式串匹配,进而求得的长度。对于上面的图来说,也就是 4(abab).此时这距离为 4 的长度因为已经比较过的缘故(P[0] 到 p[3] 这四个字符和 P[2] 到 P[5] 这四个字符相同,而P[0] 到 p[3] 已经比较过),是可以直接跳过的。

如何确定 模式串 S 应该从哪个字符开始重新比较?这便引入了部分匹配表 PMT(Partial Match Table)。

 

PMT的求取

假设有字符串 S_0S_1 ... S_ {N-1} S_ {N}

那么字符串的前缀组成的集合有:予備= \ { "S_0"、 "S_0S_1"、 "S_0S_1S_2"、... "S_0S_1 ... S_ {N-1}" \}

字符串后缀组成的集合有:ポスト= \ { "S_1S_2 ... S_N"、 "S_2S_3 ... S_N" ... "S_2S_1"、 "S_1" \}

假设:私は、事前にポストでj個の\を\します

PST的定义:PST = MAX \ {I、Jの\}、\もしI = J

一个例子:babcab,那么前缀集合为{{b},{ba},{bab},{babc},{babca}},后缀集合为{{abcab},{bcab},{cab},{ab},{b}},此时两个集合的交集为{{ab},{b}},那么交集中含有元素的最多的为{ab},此时 PST=2

 

PMT特定のアルゴリズムを打つ、会場:https://blog.csdn.net/x__1998/article/details/79951598#commentsedit

PMT核となるアイデアを打ちます:

サフィックスを求めて、そして次の行の先頭行は、上記の比較の接頭辞と接尾辞であるように、文字列の中で、それは、位置比較にシフトしています。すなわち:

KMPアルゴリズムコード

#include<iostream>
#include<string>
#define MAX_SIZE 1024
using namespace std;
int next[MAX_SIZE];
void get_next(string s1)
{
	int len = s1.length();
	int i = 0, j = -1;
	next[0] = -1;                       // 初始next[0]为-1,代表0号字符前无前缀后缀匹配
	while(i < len){
		if(j==-1 || s1[i]==s1[j]){      // 如果是next[1]则为0
			i++;
			j++;
			next[i] = j;
		}
		else{
			j = next[j];                // 如果不满足前缀后缀匹配,则将j跳转到前面next指定的位置
		}
	}
}
int KMP(string s1, string s2, int pos)
{
	int len1 = s1.length();
	int len2 = s2.length();
	if(!len2 || len2>len1) return -1;
	int i = pos, j = 0;
	while(i<len1 || j<len2){
		if(j==-1 || s1[i]==s2[j]){
			i++;
			j++;
		}
		else{
			j = next[j];
		}
	}
	if(j==len2 && i<=len1) 
		return i-j+1;
	else
		return -1;
}
int main()
{
	string s1 = "ababababca";
	string s2 = "abababca";
	get_next(s2);
	cout<<KMP(s1, s2, 0)<<endl;
	return 0;
}

 

おすすめ

転載: blog.csdn.net/cprimesplus/article/details/90374592