これはおそらくアルゴリズム的に最も効率的なソリューションではありませんが、クラス設計の観点からはクリーンです。このソリューションは、「ソートされた」特定の単語を比較するアプローチを採用しています。
同じ数字に同じ文字が含まれている場合、その単語は別の単語の順列であると言えます。String
これは、単語をaから。に変換できることを意味しますMap<Character,Integer>
。このような変換は複雑さO(n)になります。ここで、実装String
の挿入にO(1)のコストがかかると仮定すると、nはの長さです。Map
にはMap
、単語で見つかったすべての文字がキーとして含まれ、値として文字の頻度が含まれます。
例。abbcはに変換されます[a->1, b->2, c->1]
bacbはに変換されます[a->1, b->2, c->1]
したがって、2つの単語が一方が他方の順列であるかどうかを知る必要がある場合は、両方をマップに変換してからを呼び出すことができますMap.equals
。
次に、テキスト文字列を繰り返し処理し、探している単語と同じ長さのすべてのサブ文字列に変換を適用する必要があります。
Inerdialによって提案された改善
このアプローチは、「ローリング」方式でマップを更新することで改善できます。
i=3
つまり、OP(サブストリング)の例のhaystackのインデックスで一致している場合xya
、マップはになります[a->1, x->1, y->1]
。干し草の山を進むときは、の文字数を減らし、haystack[i]
の数を増やしますhaystack[i+needle.length()]
。
(動作を確認するためにゼロを削除するかMap.equals()
、カスタム比較を実装するだけです。)
マックスによって提案された改善
matchedCharactersCnt
変数も導入するとどうなりますか?干し草の山の初めにそれはなります0
。マップを目的の値に変更するたびに、変数をインクリメントします。目的の値から変更するたびに、変数をデクリメントします。反復ごとに、変数が針の長さと等しいかどうかを確認します。もしそうなら-あなたは一致を見つけました。毎回完全なマップを比較するよりも高速です。
Maxが提供する擬似コード:
needle = "abbc"
text = "abbcbbabbcaabbca"
needleSize = needle.length()
//Map of needle character counts
targetMap = [a->1, b->2, c->1]
matchedLength = 0
curMap = [a->0, b->0, c->0]
//Initial map initialization
for (int i=0;i<needle.length();i++) {
if (curMap.contains(haystack[i])) {
matchedLength++
curMap[haystack[i]]++
}
}
if (matchedLength == needleSize) {
System.out.println("Match found at: 0");
}
//Search itself
for (int i=0;i<haystack.length()-needle.length();i++) {
int targetValue1 = targetMap[haystack[i]]; //Reading from hashmap, O(1)
int curValue1 = curMap[haystack[i]]; //Another read
//If we are removing beneficial character
if (targetValue1 > 0 && curValue1 > 0 && curValue1 <= targetValue1) {
matchedLength--;
}
curMap[haystack[i]] = curValue1 + 1; //Write to hashmap, O(1)
int targetValue2 = targetMap[haystack[i+needle.length()]] //Read
int curValue2 = curMap[haystack[i+needle.length()]] //Read
//We are adding a beneficial character
if (targetValue2 > 0 && curValue2 < targetValue2) { //If we don't need this letter at all, the amount of matched letters decreases
matchedLength++;
}
curMap[haystack[i+needle.length()]] = curValue2 + 1; //Write
if (matchedLength == needleSize) {
System.out.println("Match found at: "+(i+1));
}
}
//Basically with 4 reads and 2 writes which are
//independent of the size of the needle,
//we get to the maximal possible performance: O(n)