記事ディレクトリ
kmpアルゴリズム
kmpアルゴリズムは、str1(長さがn)にstr2(長さがs)が含まれているかどうかを比較するアルゴリズムです。
小さなヒント:暴力的な解決策の方法はO(sn)ですが、次の例を見てください。
aとdが異なり、文字列が一致する場合は一致しないことを示します暴力的な解決策、以下になります。
最初からもう一度一致しますが、見てくださいkmpアルゴリズム次に何をしますか?
何故ですか?
kmpアルゴリズムの本質は、文字列の独自の検索の特性を理解し、暴力的なアルゴリズムのプロセスを加速するために繰り返し一致することを回避することです。
最初の図では
acacとacacの両方が正常に一致し、str2文字列はdの前に特性を満たします
A。。。A A ... AA 。。。A
、すなわち開始および終了は、同じ(A = AC)である(... = NO)
したがって、上の図をよりよく理解できます。Part2とpart3は正常に比較され、part2とpart1も比較されます(str2の特性を理解しています)。当然、part1とpart3を比較する必要はありません。次の図に直接ジャンプできます。
str2-next配列の特性を定義します
次の配列は、str1のビットがstr2のビットと一致しない場合を示します。どのjポインタにジャンプする必要があります何?
いくつかのシーンを紹介しますが、どれにジャンプする必要がありますか?
ここで、str2のdの前の文字列は、A ... Aの特性を満たしていないため、次の図にスキップしてください。
(Jポインターは0の位置にジャンプし、iポインターは移動しません)、次の配列のnext [4] = 0
ここはどこにジャンプしますか?
str2のhがA ... Aの特性を満たす前に、次の図にジャンプすることがわかりました。
つまり、iポインターが異なり、jポインターが位置6から位置2にジャンプします。つまり、この文字列str2の場合、next [6] = 4
ここはどこにジャンプしますか???
下の画像にスキップ
(Iポインター+ 1、jポインターは移動しません。ここでは、次の配列を-1として定義します。つまり、next [0] = -1)
以下の次の配列を見てください
- 0番目のビットが等しくない場合、iポインター+ 1、jポインターは移動せず、-1に設定されます
- 最初の場所が等しくない場合は、c、aの前の文字列を見て、A ... Aの特性を満たしていない場合、jポインターが0にジャンプします。
- 2桁目が等しくない場合は、a、acの前の文字列を見てください。これは、A ... Aの特性を満たしていないため、jポインターが0にジャンプします。
- 3番目の位置が等しくない場合は、c、acaの前の文字列を見て、A ... Aの特性を満たすと、jポインターがAの後の文字列、つまり1にジャンプします。
- 4桁目が等しくない場合は、a、acacの前の文字列を見て、A ... Aの特性を満たすと、jポインターはAの次の桁である2にジャンプします。
- 5番目の位置が等しくない場合は、f、acacaの前の文字列を見て、A ... Aの特性を満たすと、jポインターはAの後ろ(aca、A、およびAのオーバーラップ)、つまり3にジャンプします。
- 6桁目が等しくない場合は、g、acacafがA ... Aの特性を満たさない前の文字列を確認すると、jポインタが0にジャンプします。
コード
public class main {
//得到next数组
//编写该函数代码是理解如何由next[i-1]得到next[i]
//next[i-1]=t 代表前t位(0~t-1) 等于后面t位(到i-2位)
//因此若s[t]==s[i-1] 则next[i]=t+1
//否则最后一位不等代表不满足A...A的特点,直接为0
public static int[] getnext(String s)
{
int[] next=new int[s.length()];
next[0]=-1;
for(int i=1;i<next.length;i++)
{
if(i==1)
{
next[1]=0;
}
else {
if(s.charAt(next[i-1])==s.charAt(i-1))
{
next[i]=next[i-1]+1;
}
else {
next[i]=0;
}
}
}
return next;
}
public static boolean kmpmatch(String s1,String s2)
{
int[] next=getnext(s2);
int i=0,j=0;
for(;i<s1.length();i++)
{
if(j==s2.length())
{
return true;
}
if(s1.charAt(i)==s2.charAt(j))
{
j+=1;
}
else
{
if(j==0)
{
i=i+1;
}
else {
j=next[j];
}
}
}
return false;
}
public static void main(String[] args)
{
System.out.println(kmpmatch("adaddefg", "ac"));
}
nextval
-
次の配列の改善、理解しやすい
-
次の配列は、jポインタがジャンプする位置を表します。s2[a]と等しくないnext [a] = bの場合、jポインタはaからbにジャンプします。s2[b] == s2 [aの場合]、待機せずに前方にジャンプするため、次の配列をジャンプの最終位置であるnextvalに変更できます。
-
例えば
-
コード
public static int[] getnextval(String s)
{
int[] next=getnext(s);
for(int i=0;i<next.length;i++)
{
if(s.charAt(i)==s.charAt(next[i]))
{
next[i]=next[next[i]];
}
}
return next;
}
kmpアプリケーション
古典的なトピック1:str1: "123456"、回転する単語がたくさんあります、 "123456"、 "23456、
" 345612 "...
str1がstr2の回転する単語であるかどうかを判断しますか?
str1をstr1str1に拡張し、str2がサブストリングであるかどうかを確認し、ここでkmpを使用します。
古典的なトピック2:tree2がツリー
1のサブツリーであるかどうかを確認します。完全である必要があり、サブツリーには完全なルートノードが含まれています。
暴力的な解決策:
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x; }
}
public static boolean ischildtree(TreeNode root1,TreeNode root2)
{
if(root1==null||root2==null)
{
return false;
}
if(issametree(root1, root2))
{
return true;
}
return ischildtree(root1.left, root2)||ischildtree(root1.right, root2);
}
public static boolean issametree(TreeNode root1,TreeNode root2)
{
if((root1==null&&root2!=null)||(root1!=null&&root2==null))
{
return false;
}
if(root1==null&&root2==null)
{
return true;
}
if(root1.val==root2.val)
{
return issametree(root1.left, root2.left)&&issametree(root1.right, root2.right);
}
else {
return false;
}
}
本質は文字列の比較でもあり、各ツリーはバイナリツリーに入力され、配列順に格納されます。次に、kmpアルゴリズムの反対側で「文字列の後ろ」が使用されます。これは前のサブ文字列ですか?