文字列の回文サブシーケンスの数をカウントするための動的プログラミング アルゴリズムがあります。これを使用して、サブシーケンスの数 (単純に 2 n )から回文サブシーケンスの数を引くことにより、非パリンドローム サブシーケンスの数をカウントできます。O(n2)
このアルゴリズムは、OP の基準によってサブシーケンスをカウントします。結果のサブシーケンスが同じ要素を持っていても、要素を選択するために使用されるインデックスのリストに違いがある場合、2 つのサブシーケンスは異なると見なされます。
回文サブシーケンスをカウントするには、シーケンスの間隔に基づいてカウントを構築します。具体的には、次のように定義します。
Si,j
= index でS
始まり indexi
で終わる部分文字列j
(包括的)
Pi,j
= のパリンドローム部分配列の数Si,j
ここで、すべての 1 要素区間は回文なので、次のようになります。
Pi,i = 1
すべてのためにi < n
部分文字列が同じ要素 (つまり ) で開始および終了しない場合、回文部分列は次の要素で構成されます。Si ≠ Sj
含むが含まないものSi
Sj
含むが含まないものSj
Si
どちらも含まないものSi
Sj
ここで、サブシーケンスの最初と 3 番目のセットの両方が含まれていることに注意してください。一方、2 番目と 3 番目のセットの両方が含まれています。まさに3セット目。その結果:Pi,j-1
Pi+1,j
Pi+1,j-1
Pi,j = Pi+1,j + Pi,j-1 − Pi+1,j-1
もしもSi ≠ Sj
しかし、もし? その場合、開始文字と終了文字だけからなる回文部分列だけでなく、 の後に からの部分列回文が続く回文を追加する必要があります。(技術的には、空のシーケンスは回文ですが、ここでは数えません。) 追加するサブシーケンスの数は P i+1,j-1 + 1 であり、上記の式で減算された double カウントを相殺します。そう:Si = Sj
Si
Si+1,j-1
Sj
Pi,j = Pi+1,j + Pi,j-1 + 1
もし。Si = Sj
スペースを節約するために、実際に の増加する値を計算できます。最終結果 P 0,|S|-1を生成するには、これらのベクトルのうち 2 つを保持するだけで済みます。Pi,i+k for 0 ≤ i < |S|-k
k
編集:
これは小さな python プログラムです。最初のものは上記のように回文サブシーケンスの数を計算し、ドライバーは非回文サブシーケンスの数を計算します (つまり、0 個以上の要素を削除して非回文を生成する方法の数; 元のシーケンスが回文である場合)。の場合、1 つまたは複数の要素を削除する方法の数です。)
# Count the palindromic subsequences of s
def pcount(s):
length = len(s)
p0 = [0] * (length + 1)
p1 = [1] * length
for l in range(1, length):
for i in range(length - l):
p0[i] = p1[i]
if s[i] == s[i + l]:
p1[i] += p1[i+1] + 1
else:
p1[i] += p1[i+1] - p0[i+1]
# The "+ 1" is to account for the empty sequence, which is a palindrome.
return p1[0] + 1
# Count the non-palindromic subsequences of s
def npcount(s):
return 2**len(s) - pcount(s)