有没有一种数学上解决UVA 417的方法?

时间:2019-01-30 10:22:12

标签: c++ algorithm

我正在尝试解决UVA 417,但无法解决。我见过的所有解决方案都首先生成所有可能的值,将它们存储在映射中,然后搜索以查找所需的字符串。这对我来说似乎很微不足道。有没有办法数学上解决这个问题?

考虑输入“ abc”。 如果没有规定每个后续字符都应大于当前字符的条件,则可以通过简单地计算1 * 26 ^ 2 + 2 * 26 ^ 1 + 3 * 26 ^ 0来解决。难道没有办法以类似的方式解决原始问题吗?

我包括了我在网上找到的现有解决方案的代码:

#include <iostream>
#include <string>
#include <map>
#include <queue>

using namespace std;

map<string, int> M;

void generate_positions(){
    queue<string> Q;
    for(char c='a';c<='z';c++) Q.push(string(1,c));

    string s;

    int cont=1;

    while(!Q.empty()){
        s=Q.front();
        Q.pop();

        M[s]=cont;
        cont++;

        if(s.size()==5) continue;

        for(char c=s[s.size()-1]+1;c<='z';c++) Q.push(s+c);
    }
}

int main(){
    generate_positions();

    string s;
    map<string, int> :: iterator it;

    while(cin>>s){
        it=M.find(s);
        if(it==M.end()) cout<<0<<endl;
        else cout<<it->second<<endl;
    }

    return 0;
}

2 个答案:

答案 0 :(得分:5)

1。 TL / DR:

让我们定义一个字母 A = {a,b,c,... z} 和它的双射字母 A'= {1、2、3、26 ... 26} | A | = \ | A'| = 26。

然后让 w 表示由字符 w 0 ,w 1 ,……w | w |组成的单词。 –1 ∈A',顺序如下:

w = w | w | –1 ……w 1 w 0 ,其中 | w | 表示字符长度。

现在,functional P(w):(A') | A'| →→可以转换单词 w,|。 w | ≤| A'| ,其位置如下:

w k A 音译为 A'由用户决定。

2。实施:

#include <stdio.h>
#include <math.h>

long bin26(long n, long k) {
    static const char nprm[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
    long root = (long)sqrt(n), coef[sizeof(nprm) / sizeof(*nprm)] = {0};
    long indx, iter, prod, curr, prev, ncur, kcur;

    if ((n <= 0) || ((k = (k > n / 2)? n - k : k) <= 0))
        return (n > 0) && (k >= 0);
    for (indx = iter = 0; (curr = (long)nprm[iter++]) <= n; )
        if (curr > n - k)
            coef[indx++] = curr;
        else if (curr <= n / 2) {
            if (curr > root) {
                if ((n % curr) < (k % curr))
                    coef[indx++] = curr;
                continue;
            }
            for (ncur = n, kcur = k, prod = 1, prev = 0; ncur > 0; ) {
                if ((prev = ((ncur % curr) < (kcur % curr + prev))? 1 : 0))
                    prod *= curr;
                ncur /= curr;
                kcur /= curr;
            }
            if (prod > 1)
                coef[indx++] = prod;
        }
    for (iter = 1; indx; iter *= coef[--indx]);
    return iter;
}

int main(int argc, char *argv[]) {
    const long size = 26;
    long retn, lstr, iter;

    for (--argc; argc > 0; argc--) {
        for (iter = lstr = 0; argv[argc][lstr]; iter = argv[argc][lstr++])
            if (iter >= argv[argc][lstr]) {
                lstr = 0;
                break;
            }
        for (--lstr, iter = retn = 0; iter <= lstr; iter++)
            retn += bin26(size, iter + 1)
                 -  bin26(size - argv[argc][lstr - iter] + 'a' - 1, iter + 1);
        printf("P(%s) = %ld\n", argv[argc], retn);
    }
    return 0;
}

3。说明:

在不失一般性的前提下,我们首先将字母大小和单词长度限制为较小的值,然后尝试将问题可视化。

| A'| = 6,| w | = 3 。完整的单词列表如下所示:

 k n                        [ |  ]  be  →  14            [ | ] acf  →  28
[1|1]   a  →   1            [ |  ]  bf  →  15            [ | ] ade  →  29
[ |1]   b  →   2            [ | 3]  cd  →  16            [ | ] adf  →  30
[ |1]   c  →   3            [ |  ]  ce  →  17            [ | ] aef  →  31
[ |1]   d  →   4            [ |  ]  cf  →  18            [ |6] bcd  →  32
[ |1]   e  →   5            [ | 2]  de  →  19            [ | ] bce  →  33
[ |1]   f  →   6            [ |  ]  df  →  20            [ | ] bcf  →  34
[2|5]  ab  →   7            [ | 1]  ef  →  21            [ | ] bde  →  35
[ | ]  ac  →   8            [3|10] abc  →  22            [ | ] bdf  →  36
[ | ]  ad  →   9            [ |  ] abd  →  23            [ | ] bef  →  37
[ | ]  ae  →  10            [ |  ] abe  →  24            [ |3] cde  →  38
[ | ]  af  →  11            [ |  ] abf  →  25            [ | ] cdf  →  39
[ |4]  bc  →  12            [ |  ] acd  →  26            [ | ] cef  →  40
[ | ]  bd  →  13            [ |  ] ace  →  27            [ |1] def  →  41

括号中的左数字(我们称其为 k )显示相应单词中及其下方的字母数。正确的数字( n )依次显示第一个字母保持连续多少个单词。

显然,当 k 更改时,随后的 n -s遵循的定律也随之变化。识别模式非常容易:当 k 等于某个 K 时,其对应的 n -s只是连续地缩短了“ tails”对应于 K–1 的单词堆栈,前面带有一个新字母。与当前的 t (两次迭代均在 K t + 1 缩短“ tail”的量>),等于从第 K–1 个堆栈中剩余的剩余“ tail”中最大的 n

让我们调用第 k 个堆栈的高度 H k 。让我们也连续调用 t n ,属于第 k 个堆栈, n k < / sub> t (自然, t 编号是每个 k 的本地编号;例如, n 3 < / sub> 2 通常与 n 1 2 不同)。最重要的是,我们使 t -向后移动(无论如何都是堆栈),因此在上面的示例中, n 3 4 = 10 n 3 3 = 6 n 3 2 = 3 n 3 1 == 1

根据以前的观察和约定,

从这里可以明显看出 H k–1 = n k | n k | +1 (暂时忘记 {n k } 实际上不包含 | n k | +1 个元素)。这直接导致 [2] 中的 H k (n)的定义:

继续 [3] 中所述的 H k (n)binomial coefficients之间的联系的证明。< / p>

首先,基本身份(也称为Pascal`s Rule):

从帕斯卡定律中可以得出以下结论:

细心的读者可能已经注意到:

精细,涵盖 H 0 (n) H 1 (n)。让我们通过归纳证明其余部分。

在证明 [3] 的情况下,唯一导出 [4] 的方法就是引入 P(w)并将其表达为 H -s的条款。

对于某个特定单词 w ,获得其在单词列表中的位置 P(w)意味着找到与 w < / strong>。

查找 P 1 (w)的第一个近似值 P 1 (w),涉及将以下子堆栈的高度相加:全部 1≤k≤| w |

 ...
|w–2|
---------
|w–1|
|w–1|
|w–1|
|w–1|
|w–1|
|w–1|
---------
| w |
| w |<--- P(w)
| w |
| w |
| w |
-----<--- P₁(w)
|w+1|
 ...

根据 [5]

为什么 1≤k <| w | ?仅仅因为已经有一种计算堆栈“尾部”高度的方法,但是对于“头部”并没有设计出任何类似的方法。因此,为了细化近似值,应在 w 的位置将 H | w | 堆栈一分为二,并求出新堆栈的高度。 «tail»将从近似值中减去。

好吧,让我们从堆栈底部开始向上爬。每当 w 等于 H | w | 堆栈中的最后一个单词时, P 1 (w)< / strong>完全不需要调整,因此等于 P(w)

正在讨论的地方所用的单词由以下字符组成: W | w | –1 W | w | –2 …W 1 W 0 ,其中 W i = A' | A'| –i < / strong>-例如(22)(23)(24)(25)(26),用于 H 5 堆叠在原始 A'(或音译为 A 时简称 vwxyz )。这里主要要考虑的是,对于 H | w | 中的所有 | w | W 0 始终等于字母的最后一个字母, W 1 等于倒数第二个,等等。

好吧。现在,如果目标单词在 H | w | 中倒数第二个,该怎么办?普通来说, P 1 (w)会被调整为 –1 。倒数第二个单词需要用 –2 进行调整,依此类推,直到 W 0 用完允许的字母,即减少或等于 W 1 (如果没有 W 1 ,则等于 1 )。让我们称此调整值为 P 2 (w):P(w)= P 1 (w)–P 2 (w)。对于 | w | = ,等式可以重写如下: P(w)= P 1 (w)–((A)| –w 0

A' | A'| 不同的

W 0 已被覆盖。 W 1 和其他呢?在从 W 0 减去之前,我们首先假定它等于 A' | A'| 。在这种情况下,显然为 W 1 调整 P 1 (w)等同于减去相应的 H 2 堆栈的“尾部”,即 | w | = 2 P(w)= P 1 (w)-((| A'| –w 0 )+ H 2 (((| A'| –1)–w 1 ))。使用minuend (| A'| –1)代替 | A'| ,因为如前所述, W 的最大可能值1 等于 A' | A'| –1 == | A'| – 1 。相同的规则适用于 W 2 H 3 堆栈, H 3 ((|| A'| –2)–w 2 )和所有其他归纳字符。

考虑到(| A'| –w 0 )= H 1 (| A'| –w 0 < / sub>)

其中,考虑到 P(w)= P 1 (w)–P 2 (w),再加上 [6] ,最后引出 [4]

答案 1 :(得分:0)

由于需要“一种数学上解决问题的方法”,因此我提供了一个伪算法。

正如已经提到的,我们不能对输入“ abc”使用“ 1 * 26 ^ 2 + 2 * 26 ^ 1 + 3 * 26 ^ 0”之类的内容。但是,这将是起点。

从该数字中我们将减去不允许的组合数(例如“ ba”),请注意不要相减两次(“ ba”和“ bba”计数为1)。

整个想法是,我们可以“轻松地”计算出多少个字母序列无效,而不是多少个字母序列有效。计算是逐个位置进行的(字母一个字母)。