我正在尝试解决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;
}
答案 0 :(得分:5)
让我们定义一个字母 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'由用户决定。
#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;
}
在不失一般性的前提下,我们首先将字母大小和单词长度限制为较小的值,然后尝试将问题可视化。
让 | 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 | = 1>,等式可以重写如下: 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)。
整个想法是,我们可以“轻松地”计算出多少个字母序列无效,而不是多少个字母序列有效。计算是逐个位置进行的(字母一个字母)。