主頁 > 移動端開發 > 按給定順序排名

按給定順序排名

2022-10-27 06:55:51 移動端開發

我得到了一個n長度字串,我需要在所有n長度字串的字母順序中找到它的排名。例如,假設我已經被給定了, "ABC"那么n=4排序將是這樣{ABCD, ABCE, ABCF ....}(考慮到我們從 0 開始)。同樣,因為它將是 1。現在,我如何找到 的等級"ABCD"0"ABCE""YTZ"

uj5u.com熱心網友回復:

Craig Estey 已經想出了一個很好的答案,但我想我會添加到他的排列示例中。如果您想找到一個 n 長度的單詞按字母順序排列的位置,您不必增加以 26 為基數的數字。

這是一些代碼,可以找到 n 長度單詞所在的正確索引:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* Convert base 26 to base 10
 * Base 26 is represented here as A (0), B (1) ... Z (25)
 */
unsigned ts_to_ten(char *buf, size_t len)
{
    unsigned ret = 0;
    for (size_t i = 0; i < len; i  ) {
        ret *= 26;
        ret  = (buf[i] - 'a');
    }

    return ret;
}

int main(int argc, char **argv)
{
    if (argc != 3) {
        fprintf(stderr, "fatal: requires 3 arguments.\n");
        exit(EXIT_FAILURE);
    }
    
    char *start_text = argv[1];
    char *end_text = argv[2];
    size_t n;

    assert((n = strlen(start_text)) == strlen(end_text));
    
    unsigned start = ts_to_ten(start_text, n);
    unsigned end = ts_to_ten(end_text, n);

    printf("%u\n", end - start);
}

這個程式接受起始詞和結束詞。起始詞是被認為是第一次迭代的詞(或 0),或在問題的情況下為“abc”。根據問題,結束詞將是您想要獲得迭代的詞或“zxy”。

因此,如果將兩個單詞都轉換為數字并從結尾減去開頭,您將按字母順序獲得結尾單詞與開頭單詞之間的距離。

uj5u.com熱心網友回復:

好的,正如我上面提到的,這是一個“列印所有數字寬的以 26 為基數的n數字”問題。

雖然我們可以使用字串(例如ABC,DEF等),但 [IMO] 更容易轉換為以 26 為基數的數字。

您希望號碼中的數字是唯一的也就是說,數字的組合而不是排列

排列實際上更容易,所以讓我們從那個開始......


這是一個對所有可能的排列進行暴力生成的程式。它被[大量]注釋:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

int ndig;                           // number of digits in string

// the alphabet
#define ALFMAX      26
const char *alf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int opt_v;                          // verbose mode

// diginc -- increment the base 26 number (ndig digits wide)
// RETURNS: carry out
int
diginc(char *digits)
// digits -- base 26 number (ndig digits wide)
{
    int cout = 0;

    for (int idx = ndig - 1;  idx >= 0;  --idx) {
        int curdig = digits[idx];

        curdig  = 1;

        // did we overflow/wrap the digit?
        cout = (curdig >= ALFMAX);

        // no -- store new number and exit
        if (! cout) {
            digits[idx] = curdig;
            break;
        }

        // wrap digit back to zero
        digits[idx] = 0;
    }

    return cout;
}

// digdecode -- printable string (A-Z) to base 26 number (0-25)
void
digdecode(char *digits,const char *str)
// digits -- base 26 number (ndig digits wide)
// str -- string (A-Z)
{

    for (int idx = 0;  idx < ndig;    idx)
        digits[idx] = str[idx] - 'A';
}

// digprint -- convert base 26 number to printable form (e.g. A-Z)
// RETURNS: pointer to string
char *
digprint(const char *digits)
// digits -- base 26 number (ndig digits wide)
{
    int idx;
    static char str[1000];

    for (idx = 0;  idx < ndig;    idx)
        str[idx] = digits[idx]   'A';

    str[idx] = 0;

    return str;
}

// digmatch -- compare two base 26 numbers for equality
// RETURNS: 1=match, 0=mismatch
int
digmatch(const char *diglhs,const char *digrhs)
// diglhs -- base 26 number (ndig digits wide)
// digrhs -- base 26 number (ndig digits wide)
{
    int match = 0;

    for (int idx = 0;  idx < ndig;    idx) {
        match = (diglhs[idx] == digrhs[idx]);
        if (! match)
            break;
    }

    return match;
}

int
main(int argc,char **argv)
{

    --argc;
      argv;

    setlinebuf(stdout);

    // get options
    for (;  argc > 0;  --argc,   argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp  = 2;
        switch (cp[-1]) {
        case 'v':
            opt_v = ! opt_v;
            break;
        }
    }

    // candidate string (e.g. ABD or ZYX)
    const char *str;

    ndig = -1;
    switch (argc) {
    case 2:
        ndig = atoi(argv[1]);
        // fall through

    case 1:
        str = argv[0];
        break;

    default:
        sysfault("usage: <str> [n]\n");
        break;
    }

    // number of digits in string
    if (ndig < 0)
        ndig = strlen(str);

    printf("ndig=%d str='%s'\n",ndig,str);

    // convert string to base 26 digit vector
    char expdig[ndig];
    digdecode(expdig,str);

    // sequence index
    int seqidx = 0;

    // current base 26 number
    char curdig[ndig];

    // initialize to all zeros
    for (int idx = 0;  idx < ndig;    idx)
        curdig[idx] = 0;

    while (1) {
        // does the current digit vect match the expected/desired vector?
        int match = digmatch(curdig,expdig);

        // yes [or verbose mode] -- print the entry
        if (match || opt_v) {
            printf("d: %s",seqidx,digprint(curdig));

            if (match)
                printf(" MATCH");

            printf("\n");
        }

        // increment the base 26 number
        if (diginc(curdig))
            break;

          seqidx;
    }

    return 0;
}

這是輸出ABD

ndig=3 str='ABD'
        29: ABD MATCH

這是輸出ZYX

ndig=3 str='ZYX'
     17547: ZYX MATCH

請注意,盡管組合學大師可能有更好/更快的演算法,但我們可以通過計算頻率表并拒絕任何具有重復數字的數字來獲得組合。因此,這是對前面示例的輕微擴展:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

int ndig;                           // number of digits in string

// the alphabet
#define ALFMAX      26
const char *alf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int opt_u;                          // unique (combination) mode
int opt_v;                          // verbose mode

// freqtab -- create frequency table
// RETURNS: 1=duplicate detected
int
freqtab(const char *digits,int *freq)
{
    int dupflg = 0;

    for (int idx = 0;  idx < ndig;    idx) {
        int curdig = digits[idx];

        dupflg = freq[curdig];
        if (dupflg)
            break;

        freq[curdig]  = 1;
    }

    return dupflg;
}

// reject -- reject number if digits are duplicated
// RETURNS: 1=reject/dup, 0=unique
int
reject(const char *digits,int *freq)
{

    if (freq == NULL) {
        freq = alloca(sizeof(int) * ALFMAX);

        for (int idx = 0;  idx < ALFMAX;    idx)
            freq[idx] = 0;
    }

    // create frequency table
    int dupflg = freqtab(digits,freq);

    return dupflg;
}

// diginc -- increment the base 26 number (ndig digits wide)
// RETURNS: carry out
int
diginc(char *digits)
// digits -- base 26 number (ndig digits wide)
{
    int cout = 0;

    for (int idx = ndig - 1;  idx >= 0;  --idx) {
        int curdig = digits[idx];

        curdig  = 1;

        // did we overflow/wrap the digit?
        cout = (curdig >= ALFMAX);

        // no -- store new number and exit
        if (! cout) {
            digits[idx] = curdig;
            break;
        }

        // wrap digit back to zero
        digits[idx] = 0;
    }

    return cout;
}

// digdecode -- printable string (A-Z) to base 26 number (0-25)
void
digdecode(char *digits,const char *str)
// digits -- base 26 number (ndig digits wide)
// str -- string (A-Z)
{

    for (int idx = 0;  idx < ndig;    idx)
        digits[idx] = str[idx] - 'A';
}

// digprint -- convert base 26 number to printable form (e.g. A-Z)
// RETURNS: pointer to string
char *
digprint(const char *digits)
// digits -- base 26 number (ndig digits wide)
{
    int idx;
    static char str[1000];

    for (idx = 0;  idx < ndig;    idx)
        str[idx] = digits[idx]   'A';

    str[idx] = 0;

    return str;
}

// digmatch -- compare two base 26 numbers for equality
// RETURNS: 1=match, 0=mismatch
int
digmatch(const char *diglhs,const char *digrhs)
// diglhs -- base 26 number (ndig digits wide)
// digrhs -- base 26 number (ndig digits wide)
{
    int match = 0;

    for (int idx = 0;  idx < ndig;    idx) {
        match = (diglhs[idx] == digrhs[idx]);
        if (! match)
            break;
    }

    return match;
}

int
main(int argc,char **argv)
{

    --argc;
      argv;

    setlinebuf(stdout);

    // get options
    for (;  argc > 0;  --argc,   argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp  = 2;
        switch (cp[-1]) {
        case 'u':
            opt_u = (*cp != 0) ? atoi(cp) : 1;
            break;

        case 'v':
            opt_v = (*cp != 0) ? atoi(cp) : 1;
            break;
        }
    }

    // candidate string (e.g. ABD or ZYX)
    const char *str;

    ndig = -1;
    switch (argc) {
    case 2:
        ndig = atoi(argv[1]);
        // fall through

    case 1:
        str = argv[0];
        break;

    default:
        sysfault("usage: <str> [n]\n");
        break;
    }

    // number of digits in string
    if (ndig < 0)
        ndig = strlen(str);

    printf("ndig=%d str='%s'\n",ndig,str);

    // convert string to base 26 digit vector
    char expdig[ndig];
    digdecode(expdig,str);

    // sequence index
    int seqidx = 0;

    // current base 26 number
    char curdig[ndig];

    // initialize to all zeros
    for (int idx = 0;  idx < ndig;    idx)
        curdig[idx] = 0;

    int rejflg = 0;
    int match = 0;

    while (1) {
        if (opt_u)
            rejflg = reject(curdig,NULL);

        // does the current digit vect match the expected/desired vector?
        if (rejflg)
            match = 0;
        else
            match = digmatch(curdig,expdig);

        // yes [or verbose mode] -- print the entry
        do {
            // don't print rejections
            if (opt_v < 2) {
                if (rejflg)
                    break;
            }

            // don't print unless we have a match or using verbose mode
            if (! (match || opt_v))
                break;

            printf("d: %s",seqidx,digprint(curdig));

            if (match)
                printf(" MATCH");

            if (rejflg)
                printf(" REJECT");

            printf("\n");
        } while (0);

        // increment the base 26 number
        if (diginc(curdig))
            break;

        // increment sequence index if (unique)
        seqidx  = (! rejflg) || (opt_v >= 2);
    }

    return 0;
}

這是輸出-u ABD

ndig=3 str='ABD'
         1: ABD MATCH

這是輸出-u ZYX

ndig=3 str='ZYX'
     15599: ZYX MATCH

請注意,上面的效率有點低。這是因為頻率表在每次迭代時都會從頭開始重新計算。

請注意,freqtabreject采取額外的freq論點。那是對未來的規劃。

如果我們傳遞一個實際的陣列,對函式進行一些修改并添加兩個額外的函式,我們可以快速調整頻率表,而無需每次都從頭開始創建它。


更新:

這看起來很整潔!只是幾個問題,所以正如你所說,它有點低效但是,如果 ndig 超過 5,該程式似乎可以作業。

是的,它起作用,但 [正如我所說的] 它會很慢。我在下面撰寫了更快的版本。

現在,我知道一些 C,但不能完全理解你是如何實作它的,如果這是預期的行為,我的錯 - only_ranch

這里有很多東西要解開。我已經對來源進行了大量注釋[評論]。但是,仍然可能需要一些時間來消化。

如果一個人不熟悉 C 編碼,則尤其如此。語法和含義的一些基本結構(例如if, while,for等)可能 [尚未] 清楚 [一目了然]。

這可能需要一些額外的研究。if(例如)您可能需要更長的時間(例如一兩分鐘)才能得到它,而不是查看一個宣告并“立即”理解它。您可能需要檢查幾次。

然后是演算法本身。同樣,可能需要反復幾次才能理解事物。

有時鉛筆和紙在這里可以成為你最好的朋友......

在剛開始時很正常。

在您擁有“啊哈!”之前,這似乎很困難/難以處理。片刻。然后,理解將“涌入”。

這就是我剛學習編碼時的感覺。

這就是為什么我從更簡單的排列版本(具有重復字符)開始。然后,執行組合的第二個版本(其中數字唯一的)。

有一句古老的編程格言 [來自 https://en.wikipedia.org/wiki/The_Elements_of_Programming_Style]:在讓它變得更快之前先做好它。

以下是具有“更智能”頻率表計算的版本。我不得不:

  1. struct為頻率表創建一個
  2. 更改表創建函式(從 重命名freqtabftabnew)。
  3. 添加兩個功能:ftabaddftabsub進行增量更改。
  4. 根據需要修改diginc呼叫ftabadd/ftabsub
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

int ndig;                           // number of digits in string

// the alphabet
#define ALFMAX      26

int opt_u;                          // unique (combination) mode
int opt_v;                          // verbose mode

// frequency table
typedef struct {
    char ftab_duptot;               // total number of dup _cells_ in ftab_freq
    char ftab_freq[ALFMAX];         // frequency counts
} ftab_t;

// ftabadd -- add to frequency table
// RETURNS: 1=duplicate detected
int
ftabadd(int curdig,ftab_t *ftab)
// curdig -- digit to add/remove from table
// ftab -- pointer to frequency table
// inc --  1=increment, -1=decrement
{
    int cntold = ftab->ftab_freq[curdig];

    // bump up the bucket count
    ftab->ftab_freq[curdig] = cntold   1;

    // this bucket just became a dup
    if (cntold == 1)
        ftab->ftab_duptot  = 1;

    return ftab->ftab_duptot;
}

// ftabsub -- subtract from frequency table
// RETURNS: 1=duplicate detected
int
ftabsub(int curdig,ftab_t *ftab)
// curdig -- digit to add/remove from table
// ftab -- pointer to frequency table
// inc --  1=increment, -1=decrement
{
    int cntold = ftab->ftab_freq[curdig];

    // bump up the bucket count
    ftab->ftab_freq[curdig] = cntold - 1;

    // this bucket just became unique [again]
    if (cntold == 2)
        ftab->ftab_duptot -= 1;

    return ftab->ftab_duptot;
}

// ftabnew -- create frequency table
// RETURNS: 1=duplicate detected
int
ftabnew(const char *digits,ftab_t *ftab)
{

    if (ftab == NULL)
        ftab = alloca(sizeof(*ftab));

    // zero out the counts
    ftab->ftab_duptot = 0;
    for (int idx = 0;  idx < ALFMAX;    idx)
        ftab->ftab_freq[idx] = 0;

    for (int idx = 0;  idx < ndig;    idx) {
        int curdig = digits[idx];
        ftabadd(curdig,ftab);
    }

    return ftab->ftab_duptot;
}

// diginc -- increment the base 26 number (ndig digits wide)
// RETURNS: carry out
int
diginc(char *digits,ftab_t *ftab)
// digits -- base 26 number (ndig digits wide)
{
    int cout = 0;

    for (int idx = ndig - 1;  idx >= 0;  --idx) {
        int digold = digits[idx];

        // remove value from frequency table
        ftabsub(digold,ftab);

        // get new digit value
        int dignew = digold   1;

        // did we overflow/wrap the digit?
        cout = (dignew >= ALFMAX);

        // no -- store new number and exit
        if (! cout) {
            digits[idx] = dignew;
            ftabadd(dignew,ftab);
            break;
        }

        // wrap digit back to zero
        dignew = 0;
        digits[idx] = dignew;
        ftabadd(dignew,ftab);
    }

    return cout;
}

// digdecode -- printable string (A-Z) to base 26 number (0-25)
void
digdecode(char *digits,const char *str)
// digits -- base 26 number (ndig digits wide)
// str -- string (A-Z)
{

    for (int idx = 0;  idx < ndig;    idx)
        digits[idx] = toupper(str[idx]) - 'A';
}

// digprint -- convert base 26 number to printable form (e.g. A-Z)
// RETURNS: pointer to string
char *
digprint(const char *digits)
// digits -- base 26 number (ndig digits wide)
{
    int idx;
    static char str[1000];

    for (idx = 0;  idx < ndig;    idx)
        str[idx] = digits[idx]   'A';

    str[idx] = 0;

    return str;
}

// digmatch -- compare two base 26 numbers for equality
// RETURNS: 1=match, 0=mismatch
int
digmatch(const char *diglhs,const char *digrhs)
// diglhs -- base 26 number (ndig digits wide)
// digrhs -- base 26 number (ndig digits wide)
{
    int match = 0;

    for (int idx = 0;  idx < ndig;    idx) {
        match = (diglhs[idx] == digrhs[idx]);
        if (! match)
            break;
    }

    return match;
}

int
main(int argc,char **argv)
{

    --argc;
      argv;

    setlinebuf(stdout);

    // get options
    for (;  argc > 0;  --argc,   argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp  = 2;
        switch (cp[-1]) {
        case 'u':
            opt_u = (*cp != 0) ? atoi(cp) : 1;
            break;

        case 'v':
            opt_v = (*cp != 0) ? atoi(cp) : 1;
            break;
        }
    }

    // candidate string (e.g. ABD or ZYX)
    const char *str;

    ndig = -1;
    switch (argc) {
    case 2:
        ndig = atoi(argv[1]);
        // fall through

    case 1:
        str = argv[0];
        break;

    default:
        sysfault("usage: <str> [n]\n");
        break;
    }

    // number of digits in string
    if (ndig < 0)
        ndig = strlen(str);

    printf("ndig=%d str='%s'\n",ndig,str);

    // convert string to base 26 digit vector
    char expdig[ndig];
    digdecode(expdig,str);

    // check expected string for dups
    if (ftabnew(expdig,NULL))
        sysfault("main: str has duplicate chars\n");

    // sequence index
    int seqidx = 0;

    // current base 26 number
    char curdig[ndig];

    // initialize to all zeros
    for (int idx = 0;  idx < ndig;    idx)
        curdig[idx] = 0;

    // create frequency table
    // NOTE: we _will_ start with dups
    ftab_t *ftab = alloca(sizeof(*ftab));
    ftabnew(curdig,ftab);

    int rejflg = 0;
    int match = 0;

    while (1) {
        if (opt_u)
            rejflg = ftab->ftab_duptot;

        // does the current digit vect match the expected/desired vector?
        if (rejflg)
            match = 0;
        else
            match = digmatch(curdig,expdig);

        // yes [or verbose mode] -- print the entry
        do {
            // don't print rejections
            if (opt_v < 2) {
                if (rejflg)
                    break;
            }

            // don't print unless we have a match or using verbose mode
            if (! (match || opt_v))
                break;

            printf("d: %s",seqidx,digprint(curdig));

            if (match)
                printf(" MATCH");

            if (rejflg)
                printf(" REJECT");

            printf("\n");
        } while (0);

        // increment the base 26 number
        if (diginc(curdig,ftab))
            break;

        // increment sequence index if (unique)
        seqidx  = (! rejflg) || (opt_v >= 2);
    }

    return 0;
}

uj5u.com熱心網友回復:

組合數學問題通常最終會處理大量數字,而涉及計陣列合物件的問題的解決方案很少涉及列舉每個可能的物件并隨時計數。更不用說列舉可能物件的超集,并測驗每個物件是否符合條件。像這樣的蠻力解決方案可能更容易提出,并且它們適用于非常小的問題,但是當您將問題規模擴大一點時,您很快就會發現自己的資源不足。

例如,我用優化級別 3 編譯了這個問題的另一個答案的解決方案,并慢慢增加了要索引的字串的大小:

n 細繩 數數 用戶時間(版本 1) 用戶時間(版本 2)
3 ZYX 15599 0m0.003s 0m0.003s
4 ZYXW 358799 0m0.021s 0m0.015s
5 ZYXWV 7893599 0m0.188s 0m0.068s
6 ZYXWVU 165765599 0m4.973s 0m1.630s
7 ZYXWVUT 3315311999 2m13.501s 0m41.631s

(注意:為了避免上次呼叫中的算術溢位,我將一個變數從 更改為 。int保證表示最大為 32767 的整數,因此在組合數學中很少有用。)unsigned long longint

因此,如果您想解決中等規模的問題,您需要一個計算索引而不是計數的解決方案。在這種情況下,有一個相當簡單的計算,它是字串長度的二次方(即 O(n2))。這通常不被認為是一個很大的時間復雜度,但它肯定足以擴展這個問題的解決方案范圍,至少到你需要一個 bignum 來表示計數的地步。n=15是在沒有 bignum 支持的情況下可能的最大值,下面的代碼在不到一毫秒的時間內計算出該長度的索引。

它從一個相當直接的觀察開始,如果你有一些包含t個不同字符的集合 Σ(可能是大寫字母集合的一個子集),你可以用這些字符中的n 個組成的不同字串的數量是序列t , t -1, t -2, ...中的n項。那是因為序列中第一個字符有t種可能性,然后第二個字符有t -1 種可能性,因為選擇的第一個字符不能重復使用,然后t第三個字符的可能性為 -2,因為選擇的前兩個字符不能重復使用,依此類推,直到你有n 個字符。(如果n = t,則計算結果為t!,這是t個字符的排列數。但前n項相乘適用于n < t的任何值。(對于n=0,我們使用空序列的乘積為 1 的約定。)所以如果我們只有集合 { A, J, Q, W } 并且我們想知道兩個不同字符的序列可能有多少個,我們計算 4*3=12。很容易看出這是正確的,因為我們可以只列出可能的對:四個字符中的每個字符都有三對。

但是我們想知道某個特定字母序列的索引是什么。將該序列稱為s,我們再次假設我們有一個包含t個可用字符的集合 Σ,并且??序列s的長度為n(我們要求s中的所有字符都存在于 Σ 中。)那么問題是,有多少可能的序列(按字典順序)小于s我們可以通過添加:

  • 所有以 Σ 中的一個小于s的第一個字母的字母開頭的序列,以及

  • 如果n大于 1,則所有以s的第一個字母開頭并以從 Σ 中選擇的n -1 個不同字符繼續的序列,但不包括s的第一個字母。(如果n為 1,則沒有要添加的序列。)

請注意,該總和的第一部分只是較小字母的數量(我們必須計算)乘以來自大小為t -1的字母表中長度為n -1 的無限制序列的數量,我們已經知道如何做到這一點。此外,可以使用相同的程序計算該總和的第二部分,使用更小的字符集和更短的序列長度。所以我們可以很容易地撰寫一個遞回函式來完成這個計算,但是以動態編程風格撰寫它會更有效,從頭開始作業。也就是說,我們計算每個長度的后綴數量(按字典順序)小于s的相應后綴。對于每個后綴長度,我們使用不包括初始字符的字符集s到那個后綴。

例如,讓我們計算BED使用字母組成的序列的索引ABCDEBED我們從(后綴)的末尾開始D,計算:

  • 使用字母表ACD和后綴D,長度為 1 的序列數以小于 的字母開頭D2,字母表中小于 的字母數D,因為單個字母是長度為 1 的序列。

  • 使用字母表ACDE和后綴ED,長度為 2 的序列數以小于 的字母開頭E對于三個這樣的字母(B不在字母表中)中的每一個,我們需要從大小為 3 的字母表中長度為 1 的連續序列的數量(ACDE減去我們為開始選擇的符號)。所以這是9

  • 使用全字母表ABCDE和全后綴BED,長度為三的序列的數量小于一個字母開頭B有一個這樣的字母,并且有 4×3 連續序列,總共12 個

所以我們計算的索引BED為 2 9 12 = 23。(這可以通過寫出 60 個可能的長度為 3 的序列來驗證。但如果你手頭有 Python,你也可以快速檢查一下:

>>> sum(c < tuple('BED') for c in permutations('ABCDE', 3))
23

該計算中令人討厭的部分是在每個步驟中計算出乘數:當前字母表中有多少個字符少于第一個后綴字符(即除前綴中的字符之外的整個字母表)。下面的代碼通過掃描整個前綴并計算少于第一個后綴字符的字符數來做到這一點。這就是導致二次時間復雜度的原因,因為我們反復掃描序列的初始部分。

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Define the alphabet, which must be a set of contiguous letters. */
#define ALPH_MIN 'A'
#define ALPH_MAX 'Z'
#define ALPH_COUNT (ALPH_MAX - ALPH_MIN   1)

/* This function is called exactly once, to verify that the
 * provided string only contains letters in the alphabet, and
 * that it contains no repeated letters. The repeat check depends
 * on ALPH_COUNT being no greater than the number of bits in a
 * long (which is at least 32); it would need to be fixed for
 * larger alphabets.
 */
bool validate(const char* s) {
  unsigned long seen = 0;
  for (; *s;   s) {
    if (*s < ALPH_MIN) return false;
    if (*s > ALPH_MAX) return false;
    unsigned long flag = 1UL << (*s - ALPH_MIN);
    if (seen & flag) return false;
    seen |= flag;
  }
  return true;
}

/* Assumes that validate(s) returns true */
unsigned long long perm_index(const char* s) {
  /* The "rising factorial" (see Wikipedia). */
  unsigned long long rising_fact = 1;
  unsigned long long cumulative_index = 0;
  size_t len = strlen(s);
  while (len > 0) {
    unsigned c = s[--len];
    unsigned avail = c - ALPH_MIN;
    for (unsigned i = 0; i < len;   i)
      if (s[i] < c) --avail;
    cumulative_index  = avail * rising_fact;
    rising_fact *= ALPH_COUNT - len;
  }
  return cumulative_index;
}

int main(int argc, char* argv[]) {
  for (int i = 1; i < argc;   i) {
    const char* s = argv[i];
    if (!validate(s))
      printf("Invalid string: %s\n", s);
    else
      printf("s='%s', n=%zu, i=%llu\n", s, strlen(s), perm_index(s));
  }
  return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/520660.html

標籤:C递归辞典词典字典顺序

上一篇:遞回地展平嵌套物件的地圖

下一篇:如果直到我從reactjs中的回應中恢復資料,如何呼叫apitwise/不止一次

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more