簡體   English   中英

字符串字典編排和反演

[英]String lexicographical permutation and inversion

考慮對字符串的以下函數:

int F(string S)
{
    int N = S.size();

    int T = 0;

    for (int i = 0; i < N; i++)
        for (int j = i + 1; j < N; j++)
            if (S[i] > S[j])
                T++;

    return T;
}

長度為N且所有成對的不同字符的字符串S0的總數為N! 獨特的排列。

例如,“ bac”具有以下6個排列:

bac
abc
cba
bca
acb
cab

考慮這些N! 按字典順序的字符串:

abc
acb
bac
bca
cab
cba

現在考慮將F應用於以下每個字符串:

F("abc") = 0
F("acb") = 1
F("bac") = 1
F("bca") = 2
F("cab") = 2
F("cba") = 3

給定此排列集合的某個字符串S1,我們希望找到該集合中的下一個字符串S2,它與S1具有以下關系:

F(S2) == F(S1) + 1

例如,如果S1 ==“ acb”(F = 1)比S2 ==“ bca”(F = 1 + 1 = 2)

一種方法是從S1以后開始,並遍歷排列列表以尋找F(S)= F(S1)+1。 不幸的是,這是O(N!)。

通過S1上的O(N)函數,我們可以直接計算S2嗎?

假設S1的長度為n,則F(S1)最大值為n(n-1)/2 ,如果F(S1) = n(n-1)/2 ,則表示它是最后一個函數,並且沒有任何函數下一步,但如果F(S1) < n(n-1)/2 ,是指存在至少一個字符x比炭更大yx毗鄰y ,找到了這樣的x與最低索引,和更改x和y位置。 讓我們看一下它:

S1 ==“ACB”(F = 1),1 <3,以便有一個char x比另一個更大的炭y和其指數大於更大y ,這里最小索引xc ,並且通過第一嘗試在將取代它帶有a (小於x因此算法在此處完成)==> S2 =“ cab”,F(S2)= 2。

現在讓我們用S2駕駛室進行測試:x = b,y = a,==> S3 =“ cba”。\\

查找x並不困難,重復輸入,並將其變量名為min ,而當前訪問的字符小於min ,將min設置為新訪問的char,然后訪問下一個字符,第一次訪問的字符大於min停止迭代,這是x

這是c#中的偽代碼(但我對邊界不小心,例如在input.Substring中):

string NextString(string input)
{
    var min = input[0];
    int i=1;
    while (i < input.Length && input[i] < min)
    {
       min = input[i];
       i++;
    }

    if (i == input.Length) return "There isn't next item";

    var x = input[i], y=input[i-1];
    return input.Substring(0,i-2) + x + y + input.Substring(i,input.Length - 1 - i);

}

這是用於解決問題的算法的概述。

我假設您有一個函數直接返回第n個置換(給定n )及其反函數,即,一個函數返回給定置換的n 讓它們分別是perm(n)perm'(n)

如果我沒有弄錯的話,當您有一個由4個字母組成的字符串來置換函數F時,如下所示:

F("abcd")   = 0
F("abdc")   = 1
F(perm(3))  = 1
F(...)      = 2
F(...)      = 2
F(...)      = 3
F(perm(7))  = 1
F(...)      = 2
F(...)      = 2
F(...)      = 3
F(...)      = 3
F(...)      = 4
F(perm(13)) = 2
F(...)      = 3
F(...)      = 3
F(...)      = 4
F(...)      = 4
F(...)      = 5
F(perm(19)) = 3
F(...)      = 4
F(...)      = 4
F(...)      = 5
F(...)      = 5
F(perm(24)) = 6

換句話說,當您從3個字母變為4個字母時,您將獲得F值表的4個副本,分別將(0,1,2,3)添加到(1st,2nd,3rd,4th)副本中。 例如,在第二種情況下,您已經通過將第二個字母放在第一位來進行一次排列; 只需以與原始3個字母的字符串相同的模式將其添加到其他排列中即可。

從這個輪廓出發,編寫函數F應該不是很困難(但我現在還沒有時間)。嚴格來說,F的逆不是函數,因為它將是多值的,但是給定n ,和F(n)只有少數情況可以找到m st F(m)==F(n)+1 這些情況是:

  • n == N! 其中N是字符串中字母的數目,沒有下一個排列;
  • F(n+1) < F(n) ,尋求的解決方案是perm(n+(N-1)!)
  • F(n+1) == F(n) ,解是perm(n+2) ;
  • F(n+1) > F(n) ,解為perm(n+1)

我懷疑其中某些可能僅適用於4個字母的字符串,其中某些術語將必須針對K字母排列進行調整。

這不是O(n) ,但至少是O(n²) (其中n是排列中元素的數量,在您的示例3中)。

首先,請注意,每當您在字符串中放置一個字符時,您就已經知道F的增加意味着多少-然而,比尚未添加到字符串中的字符要小很多。

這為我們提供了另一種計算F(n)的算法:

used = set()

def get_inversions(S1):
    inv = 0
    for index, ch in enumerate(S1):
        character = ord(ch)-ord('a')
        cnt = sum(1 for x in range(character) if x not in used)
        inv += cnt
        used.add(character)
    return inv

這並不比原始版本好多少,但是在反轉F時很有用。您想知道按字典順序較小的第一個字符串-因此,復制原始字符串並僅在必要時進行更改才有意義。 當需要進行此類更改時,我們還應該以最小的數量更改字符串。

為此,讓我們使用以下信息:對於n字母的字符串,F的最大值為n(n-1)/2 如果不更改原始字符串,只要所需的求反數大於該數量,這意味着我們必須在此時交換一個字母。 Python程式碼:

used = set()

def get_inversions(S1):
    inv = 0
    for index, ch in enumerate(S1):
        character = ord(ch)-ord('a')
        cnt = sum(1 for x in range(character) if x not in used)
        inv += cnt
        used.add(character)
    return inv

def f_recursive(n, S1, inv, ign):
    if n == 0: return ""

    delta = inv - (n-1)*(n-2)/2

    if ign:
        cnt = 0
        ch = 0
    else:
        ch = ord(S1[len(S1)-n])-ord('a')
        cnt = sum(1 for x in range(ch) if x not in used)

    for letter in range(ch, len(S1)):
        if letter not in used:
            if cnt < delta:
                cnt += 1
                continue

            used.add(letter)
            if letter != ch: ign = True

            return chr(letter+ord('a'))+f_recursive(n-1, S1, inv-cnt, ign)

def F_inv(S1):
    used.clear()
    inv = get_inversions(S1)

    used.clear()
    return f_recursive(len(S1), S1, inv+1, False)


print F_inv("acb")

通過用諸如二進制索引樹之類的數據結構替換最內層的循環,也可以使其在O(n log n)運行。

您是否嘗試過交換字符串中的兩個相鄰字符? 看來可以幫助解決問題。 如果交換S [i]和S [j],其中i <j和S [i] <S [j],則F(S)增加1,因為所有其他索引對均不受此置換影響。

如果我沒記錯的話,F會計算排列的反轉次數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM