文字列照合アルゴリズム [学習アルゴリズム]

序文

2023-8-6 12:06:42

以下のコンテンツは「[学習アルゴリズム]」からのものであり
、学習とコミュニケーションのみを目的としています。

著作権

他のプラットフォームで公開する場合は、次の単語を削除してください。この
記事は最初に CSDN プラットフォームで公開されました。
著者は CSDN@日星月云です。
ブログのホームページは https://blog.csdn.net/qq_51625007 です。
他のプラットフォームで公開する場合は上記の文言を使用してください。

推薦する

28. 文字列内で最初に一致したものの下付き文字を見つけます

文字列パターンマッチング


知識内容は
第 4 章 文字列 (データ構造とアルゴリズム)から取得されます。


部分文字列の位置決め操作は、メイン文字列内の pos 文字の後に部分文字列が最初に現れる位置を見つけることです。「文字列パターン マッチング」または「文字列マッチング」とも呼ばれます。この操作は広く使用されています。たとえば、テキスト編集プログラムでは、テキスト内の特定の単語が出現する場所を見つけることが必要になることがよくあります。明らかに、この問題を解決する効果的なアルゴリズムは、テキスト編集プログラムの応答パフォーマンスを大幅に向上させることができます。文字列マッチングでは、主文字列 S は一般に「ターゲット文字列」と呼ばれ、部分文字列 T は「パターン文字列」と呼ばれます。

パターン マッチングには多くのアルゴリズムがあります。この章では、BF パターン マッチングと KMP パターン マッチングという 2 つの文字列マッチング アルゴリズムについてのみ説明します。

BFパターンマッチングアルゴリズム

[BE アルゴリズムのアイデア]
「ブルート フォース マッチング」アルゴリズム (BP アルゴリズムとも呼ばれる) とも呼ばれるブルート フォース アルゴリズムは、メイン文字列 S の pos 文字から開始し、それをパターンの最初の文字と比較します。それらが等しい場合は、後続の文字を 1 つずつ比較し続けます;そうでない場合は、メイン文字列の pos+1 文字に戻り、パターン文字列 T との比較を再度開始します。同様に、パターン文字列の各文字がメイン文字列の連続する文字列と一致するまでは、パターン マッチングが成功したとみなされ、この時点で、メイン文字列 S のパターン文字列の最初の文字が返されます。 . 位置; それ以外の場合、メイン文字列内にパターン文字列と等しい文字列が存在しないため、パターン マッチングは失敗します。

[BF アルゴリズムの説明]
メイン文字列 S の後ろの文字から始まる部分文字列とパターン文字列 T を比較する戦略は、それらを前から後ろに順番に比較することです。したがって、メイン文字列にインジケーター i を設定すると、メイン文字列 S で現在比較されている文字が示され、パターン文字列 T にインジケーター j を設定すると、パターン文字列 T で現在比較されている文字が示されます。

図 4-5 に示すように、マッチング プロセスの例が示されています。この例では、ボックスの陰影に対応する文字が、メイン文字列 S とパターン文字列 T を比較したときに等しくない不一致文字です (pos=1 と仮定)。 。

メイン文字列 S の pos 番目の文字とパターン文字列 T の最初の文字を比較し、等しい場合は後続の文字を 1 文字ずつ比較します。このとき、i++;j++; そうでない場合は、次の文字から開始します。メイン文字列の最初の文字 (i-j+2 ) とパターン文字列の最初の文字 (j=1) を比較し、その分析の詳細を図 4-6(a) に示します。

一致が成功した場合は、メイン文字列 (iT.en) を基準としたパターン文字列 T の最初の文字の位置が返され、それ以外の場合は 0 が返されます。詳細な分析については、図 4-6(b) を参照してください。 m はパターン文字列 T の長さです。

int Index(SString S,int pos,SString T)
	int i=pos,j=1;//主串从第pos开始,模式串从头开始
	while (i<=S.len&&j<=T.len){
    
    
		if(S.ch[i]==T.ch[j]){
    
    //当对应字符相等时,比较后续字符
			i++;
			j++;
		}
		else{
    
    				//当对应字符不等时
			i=i-j+2;			//主串回溯到j-j+2的位置重新比较
			j=1;				//模式串从头开始重新比较
		}
	if(j>T.len)	return i-T.len;	//匹配成功时,返回匹配起始位置
	else return 0;				//匹配失败时,返回0

[BFアルゴリズム分析]
BFアルゴリズムの考え方は比較的単純ですが、最悪の場合、アルゴリズムの時間計算量は0(n×m)になります。ここで、nとmはそれぞれメイン文字列とパターンの長さです。このアルゴリズムの主な時間の消費は、不一致後の比較位置のバックトラッキングであり、その結果、比較が多すぎます。時間の複雑さを軽減するために、バックトラックのないアルゴリズムを使用できます。

KMP パターン マッチング アルゴリズム

【KMPアルゴリズムの考え方】
Knuth-Morris-Prattアルゴリズム(略称KMP)は、DEKnuthJ.HMorrisとV.RPrattが共同で提案した改良アルゴリズムです。KMP アルゴリズムは、パターン マッチングにおける古典的なアルゴリズムですが、BF アルゴリズムと比較した場合、KMP アルゴリズムの違いは、BF アルゴリズムにおけるメイン文字列 S ポインタ i のバックトラッキングを排除することです。改良されたアルゴリズムの時間計算量は 0(n+m) です。

[KMP アルゴリズムの説明]
KMP アルゴリズムでは、照合プロセスで文字が不等であると思われる場合は常に、メイン文字列 S 内のポインタをバックトラックする必要はなく、得られた「部分一致」結果を使用してパターンを移動します。文字列を右に移動し、できるだけスライドさせた後、比較を続けます。

図 4-5 の照合処理の例を振り返ると、3 回目の照合では、文字 i=7 と j=5 が等しくない場合、再度 i=4.j=1 から比較が開始されます。しかし、注意深く観察すると、i=4 と j=1、i=5 と j=1、i=6 と j=1 での 3 つの比較は不要であることがわかります。3 番目の部分一致結果から、メイン文字列の 4 番目、5 番目、および 6 番目の文字はパターン文字列の 2.3.4 文字と等しい必要がある、つまり、それらはすべて 'bca. であると結論付けることができるためです。パターン文字列の最初の文字は「a」であるため、これらの 3 文字と比較する必要はありません。続行するには、パターン文字列を 3 文字右にスライドするだけで済みます。i=7.j=2 文字は次のとおりです。比較される。同様に、最初の一致で文字が等しくない場合は、パターンを 2 文字右に移動するだけで、i=3 および j=1 のときに文字を比較できます。したがって、図 4-7 に示すように、マッチング プロセス全体でポインタが後戻りすることはありません。

ここに画像の説明を挿入します

一般的に、メイン文字列は 'S 1 S 2 ...S n '、パターン文字列は 'T 1 T 2 ...T n ' であると想定されます。上記の例の分析から、次のことがわかります。 KMP アルゴリズムを実装するには、マッチング処理時にパターン文字列に「不一致」が発生した場合 (Si ≠ Ti )、パターン文字列のスライド距離「つまり、メイン文字列の文字 Siパターン文字列の文字 T j " が一致しない場合、" と一致する場合、パターン文字列のどの文字と、文字列の文字 Si を比較する必要があります。メイン文字列 (i ポインタはバックトラックしません) ?

このとき、主文字列の文字Siとパターンの文字T k (k<j)を比較し続けるとすると、主文字列Sとパターン文字列Tは次の関係を満たす。
S=S 1 S 2 …S i-j+1 S i- j+2 …S i-k+1 …S i-1 S i …S n
T= T 1     T 2    …T j-k+1 … T j-k+2
T= T 1      …T k-1

'T 1 T 2 ...T k-1 '='T j-k+1 T j-k+2 ...T j-1 'がパターン文字列に存在し、1 を満たすことがわかります。 <k<j の場合、マッチング処理中にj≠TSi、k 番目の文字がメイン文字列の i 番目の文字と揃うまでパターン文字列を右にスライドするだけで済みます。 SiT kの比較を続けるには、i ポインタのトレースバックは必要ありません。マッチングプロセス中に可能な限り「スライド」するには、条件を満たすより大きな k 値を選択する必要があります。

next[j]=k の場合、next[] は、パターン内の j 番目の文字がメイン文字列内の対応する文字と「不一致」の場合、パターン内の文字の位置をその文字と比較する必要があることを示します。再びメイン文字列に。これは、パターン文字列の次の関数の定義につながります。
画像の説明を追加してください

次の関数の計算はパターン文字列自体にのみ関連しており、メイン文字列とは何の関係もないことがわかります。このうち、「T 1 T 2 ...T k-1 」は、 「T 1 T 2 ...T j-1 」、「T j-k+1 T j-k+2」の真のプレフィックス部分文字列です。 ...T j-1 ' 'T 1 T 2 ...T j-1 'の真のサフィックス部分文字列です。次の関数定義のセットが空でない場合、文字列 'T 1 T 2 ...T jの真のプレフィックス部分文字列と真のサフィックス部分文字列が一致する場合、next[j] の値は最大部分文字列長 + 1 に等しくなります。-1 ' は等しい。

上記の分析により、パターン文字列「abaabcac」の次の値の計算プロセスは表 4.1 に示すように導き出されます。
(1) j=1 のとき、定義より next[1]=0 がわかる
(2) j=2 のとき、1<k<j を満たす k 値は存在せず、 next[2] =1 という定義

画像の説明を追加してください

パターン文字列「abaabcac」の次の関数値を図 4-8 に示します。

パターン string の関数を取得した後next、マッチングは次のように実行できます。 ポインタiと がjそれぞれメイン文字列Sとパターン文字列Tで現在比較されている文字を示していると仮定し、iの初期値をposjの初期値を とします1マッチング プロセス中にS i =T iの場合、i合計jはそれぞれ 1 ずつ増加しますが、それ以外の場合はi変更されず、j返されたnext[j]位置が再度比較されます (つまり、Siと T next[j]が比較されます)。文字が等しい場合、ポインタはそれぞれ次の値の位置に戻り、以下の 2 つの可能性が考えられるまで続きます。 1 つは、特定の値 ( ) に戻るときに文字が等しい場合、ポインタは一致を続けるには 1 ずつ増加します。もう 1 つは0 に戻る(つまり、パターンの最初の文字と「一致しない」) 場合、メイン文字列とパターン文字列の両方を 1 つ右にスライドする必要があります。同時に(このとき j=0、正しいタイミングに 1 つスライドしたとき、つまりパターン文字列の最初の文字)、つまりメイン文字列の次の文字 Si +1から再び比較が開始されます。そしてパターン Ti 図 4-9 は、KMP マッチング プロセスの例です (pos=1 と仮定)jnextjnextnext[next[···next[j]]]jnext值

【アルゴリズム4-13】KMPパターンマッチングアルゴリズム

int Index_KMP( SString S, in pos, SString T){
    
    
	int i=pos,i=1;						//主串从第pos开始,模式事从头开始
	while(i<=S.len && j<=Tlen){
    
    
		if(j==0||S.ch[j]==T.ch[j]){
    
    		//继续比较后续宇符
			++i;++j;
		}else{
    
    
			j=next[j];					//模式申向右滑动
		}

if(j>T.len) return i-Tlen;				//匹配成功时,返回匹配的起始位置
else relurn 0							//匹配失败时,返回0

[次のアルゴリズムの説明]
KMP アルゴリズムは既知のパターンの次の関数値に基づいて実行されますが、では、パターン文字列の次の関数値を取得するにはどうすればよいでしょうか。

定義からわかるように、next[1]=0、next[j]=k と仮定すると、'T 1 T 2 ···T k-1 '='T j-k+1 Tがあることがわかります。パターン列+2・・・Tにおけるj-kは、kこのときの値next[j+1]が1<k<jというある値を満たすためには次の2つの場合が考えられる関係である。

(1) T j =T kの場合、パターン文字列 'T 1 T 2 · · · T k-1 '='T j-k+1 T j-k+2 · · · T j 'を意味します。

T=T~1~ ··· T~j-k+1~	···	T~j-1~ T~j~ ··· T~m~
					   	        =
T=		    T~1~    ···	T~k-1~ T~k~···
			长度为k

これは、next[j+1]=k+1、つまり next[j+1]=next[j]+1 を意味します。

(2) T j ≠T kの場合、パターン文字列 'T 1 T 2 · · · T k-1 '≠'T j-k+1 T j-k+2 · · · T j 'を意味します。

このとき、次の関数の値を求める問題はパターンマッチング問題とみなすことができ、パターン文字列全体が主文字列とパターン文字列の両方になります(1<k'<k<j)。

T=T~1~ ··· T~j-k+1~	··· T~j-k'+1~ ···	T~j-1~ T~j~ ··· T~m~
					   	        ≠
T=		    T~1~    ···	T~k-k'+1~ ···   T~k-1~  T~k~ ···
T=					    T~1~     ···	T~k'-1~ T~k'-next[k]~

① T j =T k'かつ k'=next[k] の場合、next[j+1]=k'+1、つまり next[j+1]=next[k]+1 となります。 next [j+1]=next[next[j]]+1 と同等。
② T j ≠T k'の場合、T jと T next[k']の比較を続けます。つまり、T jと T next[next[k]]
を比較します。...その後、最後の j=0
になるまで繰り返します。比較が失敗した場合は、 next[j+1]=1 になります。

上記の分析により、j+1 番目の文字の次の値を計算するとき、j 番目の文字が、j 番目の文字の次の値が指す文字と等しいかどうかを確認する必要があります。

パターン列 T=abaabcac の次の値の計算プロセスは次のように推定できます。
①j=1のとき、定義よりnext[1]=0であることが分かります。

j=1
T=a
n=0

②j=2の場合、1<h<jを満たす値は存在せず、定義よりnext[2]=1が分かります。

j=12
T=ab
n=01

③ j=3 のとき、T 2 ≠ T 1であり、next[1]=0 なので、next[3]=1 となります。

j=12345678
T=abaabcac
n=011
T~2~ ≠ T~next[2]~	(T~1~)
T~2~ ? T~next[1]~	(0)
next[3]=1

④j=4の場合、T 3 =T 1なので、next[4]=next[3]+1、つまりnext[4]=2となる。

j=12345678
T=abaabcac
n=0112
  
T~3~ = T~next[3]~	(T~1~)
next[4]=next[3]+1

⑤ j=5 のとき、T 4 ≠ T 2で next[2] の値は 1 なので、T 4と T 1の比較を続けます。 T 4 =T 1なので、 next[5]=next[2] +1つまり、next[5]=2です。

j=12345678
T=abaabcac
n=01122
  
T~4~ ≠ T~next[4]~	(T~2~)
T~4~ = T~next[2]~	(T~1~)
next[5]=next[2]+1

⑥j=6のとき、T 5 =T 2なので、next[6]=next[5]+1、つまりnext[6]=3となる。

j=12345678
T=abaabcac
n=011223
  
T~5~ = T~next[5]~	(T~2~)
next[6]=next[5]+1

⑦ j=7 のとき、T 6 ≠ T 3で next[3] の値が 1 なので、T 6 と T 1 の比較を続けます。T 6 T 1next [ 1]=0 なので、next[7] =1。

j=12345678
T=abaabcac
n=0112231
  
T~6~ ≠ T~next[6]~	(T~3~)
T~6~ ≠ T~next[3]~	(T~1~)
T~6~ ? T~next[1]~	(0)
next[7]=1

⑧j=8のとき、T 7 =T 1なので、next[8]=next[7]+1、つまりnext[8]=2となる。

j=12345678
T=abaabcac
n=01122312
  
T~7~ = T~next[7]~	(T~1~)
next[8]=next[7]+1,

したがって、図 4-8 に示すように、パターン文字列の次の値が取得されます。

j=12345678
T=abaabcac
n=01122312

【アルゴリズム4-14】次のアルゴリズム

void Get_Next( SString T, int next[]){
	int j=1,k=0;
	next[1]=0;
	while(j<T.len){
		if(k==0||T.ch[j]==Tch[k] ){
			++j;
			++k;
			nexi[j]=k;
		}else{
			k=next[k];
		}
	}
}

[nextval アルゴリズムの説明]

上記で定義された次の関数には、場合によっては欠陥があります。メイン文字列を「aaabaaaab」、パターン文字列を「aaaab」とすると、パターン文字列に対応する次の関数値は次のようになります。

パターン文字列の次の値を取得した後のマッチング プロセスを図 4-10(a) に示します。

文字列マッチング プロセスから、 next[j], i=4, j=3; i=4, j= で示されるように、i=4, j=4 の場合、 S 4が T 4に等しくないことがわかります。2; i=4 と j=1 の 3 つの比較。実際、パターン内の 1 番目、2 番目、3 番目の文字と 4 番目の文字はすべて等しい (つまり、すべて a) ため、メイン文字列の 4 番目の文字と比較する必要はありませんが、パターンは比較することができます。 be i=5 と j=1 の文字を直接比較するには、4 文字を右にスワイプします。

つまり、上記の定義に従って next[j]=k が得られ、パターン文字列 で T j =T kが得られる場合、Si T jの場合、SiT kを比較する必要はありません。 T next[k]と直接比較して比較する、つまり、このときのnext[j]の値はnext[k]と同じになるはずなので、next[j]をnextval[j]に修正する。パターン文字列 でT j ≠T kの場合、 Si ≠T jの場合でも、Si と T k の比較を実行する必要があるため、nextval[j] の値は k、つまり nextval[j] の値になります。は次の[ j ] の値です。

j上記の分析により、j 番目の文字の nextval 値を計算するとき、 j 番目の文字が指す文字と j 番目のj文字が指す文字が等しいかどうかに依存しますnext等しい場合は 、nextval[j]=nextval[next[j]]そうでない場合はnextval[j]=next[j]このことから、パターン文字列T='aaaab'のnextval値の計算処理は以下のようになることが推測できる。

①j=1のとき、定義よりnextval[1]=0であることが分かります。
② j=2、nex[2]=1、T 2 =T 1のとき、nextval[2]=nextval[1]、つまり ですnextval[2]=0
③j=3、next[3]=2、T 3 =T 2のとき、nextval[3]=nextval[2]、つまりnextval[3]=0となる。
④j=4、next[4]=3、T 4 =T 3のとき、nextval[4]=nextval[3]、つまりnextval[4]=0となる。
⑤ j=5、next[5]=4、T 5 ≠ T 4のとき、nextval[5]=next[5]、つまり nextval[5]=4 となります。

パターン文字列「aaaab」の次の関数値は次のとおりです。

パターン文字列の nextval 値を取得した後のマッチング処理を図 4-10(b) に示します。

nextval関数の値を求める方法には、next配列の値に頼らず直接観察して求める方法と、前述したようにnext配列の値から推測する方法の2つがありますが、ここでは2つ目の方法のみ紹介します。 。

[アルゴリズム4-15] nextvalアルゴリズム

void Get_NextVal(SString T, int next[ ] ,int nextval[ ]){
    
    
	int j=2,k=0;
	Get_Next(T,next);//通过算法4-14获得T的next值
	nextval[1]=0;	
	while (j<=T.len )
		k=next[j];
		if(T.ch[j]==T.ch[k]) nextval[j]=nextyal[ k];
		else nextval[j]=next[j];
		j++;
	}

【KMPアルゴリズム解析】

KMP アルゴリズムは、既知のパターンの next または nextval に基づいて実行されますが、どちらかが未知の場合、KMP アルゴリズムを使用する方法はありません。next と nextval がありますが、それらの意味と機能はまったく同じであるため、next または nextval のマッチングがわかっている場合、マッチング アルゴリズムは変わりません。

通常、パターン文字列の長さ m はメイン文字列の長さ n よりもはるかに小さく、next 関数または nextval 関数を計算する時間計算量は 0(m) です。したがって、next または nextval の追加計算は、マッチング アルゴリズム全体にとって価値があります。

BF アルゴリズムの時間計算量は 0(nxm) ですが、実際の実行では m が n よりはるかに小さいことが多く、約 0(n+m) となるため、現在でも使用されています。KMP アルゴリズムは、パターン文字列とメイン文字列の間に「部分一致」が多数ある場合にのみ、BF アルゴリズムより高速です。KMP アルゴリズムの最大の特徴は、主文字列のポインタを遡る必要がないことであり、照合プロセス全体で主文字列を最初から最後まで 1 回スキャンするだけで済み、膨大な処理を行う場合に非常に効果的です。周辺機器から入力されたファイルを読み込み中に照合できます。

教科書

練習する:

abaabcac
01122312

aaaab
01234

abaabcabc
011223123

ababcabaababb
0112312342345

abcabaa
0111232

abcaabbabcabaacbacba
01112231234532211211

結果は語学の教科書と全く同じです

import java.util.Arrays;

public class KMP0 {
    
    

    public static final int MAXLEN=50;

    static class SString{
    
    
        char[] ch=new char[MAXLEN+1];//0号单元不使用
        int len;

        public SString() {
    
    
        }

        public SString(char[] ch, int len) {
    
    
            this.ch = ch;
            this.len = len;
        }
    }

    public static void main(String[] args) {
    
    
//        test1();
        test2();

    }

    private static void test2() {
    
    
//        String t0="0abaabcac";
//        String t0="0abaabcabc";
        String t0="0aaaab";

        SString t=new SString(t0.toCharArray(),t0.length()-1);

        int[] next=new int[t.len+1];//0号也不使用
        get_next(t,next);

        System.out.println(Arrays.toString(next));

        int[] nextval=new int[t.len+1];//0号也不使用
        get_nextval(t,next,nextval);
        System.out.println(Arrays.toString(nextval));

    }


    private static void test1() {
    
    
        String s0="0aaabaaaab";
        String t0="0aaaab";
        SString s=new SString(s0.toCharArray(),s0.length()-1);
        SString t=new SString(t0.toCharArray(),t0.length()-1);

        int[] next=new int[t.len+1];//0号也不使用
        get_next(t,next);
        int i = index_kmp(s, 0, t,next);
        System.out.println(i);
    }


    public static int index_kmp(SString s,int pos,SString t,int[] next){
    
    
        int i=pos,j=1;
        while (i<=s.len&&j<=t.len){
    
    
            if (j==0||s.ch[i]==t.ch[j]){
    
    
                ++i;
                ++j;
            }else {
    
    
                j=next[j];
            }
        }
        if(j>t.len) return i-t.len;
        else return 0;
    }

    public static void get_next(SString t,int[] next){
    
    
        int j=1,k=0;
        next[1]=0;
        while (j<t.len){
    
    
            if(k==0||t.ch[j]==t.ch[k]){
    
    
                ++j;
                ++k;
                next[j]=k;
            }else{
    
    
                k=next[k];
            }
        }
    }

    public static void get_nextval(SString t,int[] next,int[] nextval){
    
    
        int j=2,k=0;
        get_next(t,next);
        nextval[1]=0;
        while (j<=t.len){
    
    
            k=next[j];
            if(t.ch[j]==t.ch[k]){
    
    
                nextval[j]=nextval[k];
            }else {
    
    
                nextval[j]=next[j];
            }
            j++;
        }
    }
}

Java でアルゴリズムを実装する

公式ソリューション

class Solution {
    
    
    public int strStr(String haystack, String needle) {
    
    
        int n = haystack.length(), m = needle.length();
        if (m == 0) {
    
    
            return 0;
        }
        int[] pi = new int[m];
        for (int i = 1, j = 0; i < m; i++) {
    
    
            while (j > 0 && needle.charAt(i) != needle.charAt(j)) {
    
    
                j = pi[j - 1];
            }
            if (needle.charAt(i) == needle.charAt(j)) {
    
    
                j++;
            }
            pi[i] = j;
        }
        for (int i = 0, j = 0; i < n; i++) {
    
    
            while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
    
    
                j = pi[j - 1];
            }
            if (haystack.charAt(i) == needle.charAt(j)) {
    
    
                j++;
            }
            if (j == m) {
    
    
                return i - m + 1;
            }
        }
        return -1;
    }
}


Java APIを呼び出す

class Solution {
    
    
    public int strStr(String haystack, String needle) {
    
    
       return haystack.indexOf(needle);
    }
}

リファレンス Java API

BFアルゴリズムを使用するだけです

class Solution {
    
    
    public static int strStr(String haystack, String needle) {
    
    
        char[] haystackValue=haystack.toCharArray();
        char[] needleValue=needle.toCharArray();
        return index(haystackValue,needleValue);
    }

    public static int index(char[] source, char[] target) {
    
    
        int sourceOffset=0;
        int targetOffset=0;
        int sourceCount=source.length;
        int targetCount=target.length;
        int max=sourceOffset+(sourceCount-targetCount);

        char first=target[targetOffset];

        for(int i=sourceOffset;i<=max;i++){
    
    
            //找第一个字符
            if (source[i] != first) {
    
    
                while (++i <= max && source[i] != first);
            }
            //匹配剩余的部分
            if(i<=max){
    
    
                int j=i+1;
                int end=j+targetCount-1;
                for (int k = targetOffset+1; j < end&&source[j]==target[k]; j++,k++);

                if(j==end){
    
    
                    return i-sourceOffset;
                }
            }
        }

        return -1;
    }
}

BFアルゴリズム

class Solution {
    
    
    public int strStr(String haystack, String needle) {
    
    
        return indexWithBF(haystack,0,needle);
    }

    public static int indexWithBF(String s,int pos,String t){
    
    
        int i=pos,j=0;//主串从pos开始,模式串从头开始
        while(i<s.length()&&j<t.length()){
    
    
            if(s.charAt(i)==t.charAt(j)){
    
    //当对应字符相等时,比较后续字符
                i++;
                j++;
            }else{
    
                          //对应字符不等时
                i=i-j+1;                //主串回溯到i-j+1的位置重写比较
                j=0;                    //模式串从头开始重写比较
            }
        }
        if(j>=t.length()) return i-t.length();  //匹配成功时,返回匹配起始位置
        else return -1;                         //匹配失败时,返回-1
    }
}

KMPアルゴリズム

class Solution {
    
    
    public int strStr(String haystack, String needle) {
    
    
        return indexWithKMP(haystack,0,needle);
    }

    public static int indexWithKMP(String s,int pos,String t){
    
    
        int[] next=next(t);

        int i=pos,j=0;                      //主串从pos开始,模式串从头开始
        while(i<s.length()&&j<t.length()){
    
    
            if(j==-1||s.charAt(i)==t.charAt(j)){
    
    //继续比较后续字符
                i++;
                j++;
            }else{
    
                          //模式串向右滑动
                j=next[j];
            }
        }
        if(j>=t.length()) return i-t.length();  //匹配成功时,返回匹配起始位置
        else return -1;                         //匹配失败时,返回-1
    }

    public static int[] next(String t){
    
    
        int len=t.length();
        int[] next=new int[len+1];
        int j=0,k=-1;
        next[0]=-1;
        while (j<len){
    
    
            if(k==-1||t.charAt(j)==t.charAt(k)){
    
    
                j++;
                k++;
                next[j]=k;
            }else{
    
    
                k=next[k];
            }
        }
        return next;
    }
}

C でアルゴリズムを実装する

KMPアルゴリズム

int strStr(char* haystack, char* needle) {
    
    
    int n = strlen(haystack), m = strlen(needle);
    if (m == 0) {
    
    
        return 0;
    }
    int pi[m];
    pi[0] = 0;
    for (int i = 1, j = 0; i < m; i++) {
    
    
        while (j > 0 && needle[i] != needle[j]) {
    
    
            j = pi[j - 1];
        }
        if (needle[i] == needle[j]) {
    
    
            j++;
        }
        pi[i] = j;
    }
    for (int i = 0, j = 0; i < n; i++) {
    
    
        while (j > 0 && haystack[i] != needle[j]) {
    
    
            j = pi[j - 1];
        }
        if (haystack[i] == needle[j]) {
    
    
            j++;
        }
        if (j == m) {
    
    
            return i - m + 1;
        }
    }
    return -1;
}

やっと

私たち全員に明るい未来があります

大学院受験の成功を祈り、
仕事での
成功を祈り、欲しいものを手に入れて
ください、いいね、集めて、フォローしてください。

おすすめ

転載: blog.csdn.net/qq_51625007/article/details/132129456