まず、文字列のS
長さ(m)
とパターンのT
長さの境界を知りたい(n)
です。
一般的な考え方は 1 つありますが、それに基づくソリューションの複雑さはパターンの長さに依存します。O(m)
複雑さはO(m*n^2)
、短いパターンとlength<=100
長いO(n)
パターンの場合で異なります。
算術の基本定理は、すべての整数を素数の積として一意に表すことができると述べています。
アイデア - あなたのアルファベットは英字だと思います。したがって、アルファベットのサイズは 26 です。最初の文字を最初のプライムに、2 番目の文字を 2 番目のプライムに、というように置き換えましょう。次の置換を意味します:
a->2
b->3
c->5
d->7
e->11 など。
ある文字列の文字に対応する素数の積を としましょうprime product(string)
。たとえば、はprimeProduct(z)
そのまま26 番目の素数になります。101
101
primeProduct(abc)
2*3*5=30
primeProduct(cba)
5*3*2=30
なぜ素数を選ぶのか? a ->2; を置き換えると、b ->3、c->4、例 4 を解読することはできません - それは "c" なのか "aa" なのか。
短いパターンの場合の解決策: 文字列 S の場合、すべてのプレフィックスの線形時間素数積を計算する必要があります。つまり、 、、のような配列 A を作成する必要があります。実装例:A[0] = primeProduct(S[0])
A[1] = primeProduct(S[0]S[1])
A[N] = primeProduct(S)
A[0] = getPrime(S[0]);
for(int i=1;i<S.length;i++)
A[i]=A[i-1]*getPrime(S[i]);
パターン T を検索します。primeProduct(T) を計算します。パターンと同じ長さを持つ Sのすべてについて'windows'
、primeProduct と primeProduct(pattern) を比較します。currentWindow がパターンと等しい場合、または currentWindow がパターンのスクランブル形式 (アナグラム) である場合、primeProducts は同じになります。
重要な注意点!S の任意の部分文字列に対してprimeProduct を高速に計算するために、配列 A を用意しました。= = ;primeProduct of(S[i],S[i+1],...S[j])
getPrime(S[i])*...*getPrime(S[j])
A[j]/A[i-1]
'zzzzzzzzz'
複雑さ: パターンの長さが<=9 の場合101^9<=MAX_LONG_INT
、すべての計算は標準の long 型に適合し、複雑さは O(N)+O(M) です。ここで、N はパターンの primeProduct を計算するためのもので、M は のすべてのウィンドウで反復処理を行いますS
。length<=100 の場合、mul/div の長い数値の複雑さを追加する必要があるため、複雑さが になりO(m*n^2)
ます。長さ 101^長さは O(N) mul/div のような長い数値はO(N^2)
長さが 1000 以上の長いパターンの場合は、ハッシュ マップ (素数、次数) を格納することをお勧めします。プレフィックスの配列はハッシュ マップの配列になり、A[j]/A[i-1]
トリックは differenceBetween(A[j] と A[i-1] ハッシュマップのキー セット) になります。