有一个字符串由多个变量拼接而成: "$x1$x2$x3$x4$x5$x6..." ,这个列表可以一直延伸到 $x10000,而每个变量都至少有一种可能的取值,为了简化问题,每个变量不会再引用别的变量。
现在,给定一个字符串T,判断这个字符串是否能由上面这个 "$x1$x2$x3$x4$x5$x6..." 生成。
我们可以算出,总的可能性(最多)是 n1*n2*n3*n4*n5*n6...。
因为现实中不会有太极端的情况,我们可以将这些字符串全部扩展出来,放入一个hash table。但是,如何得到这些扩展结果呢?
这是一个笛卡尔积的问题,可以用最简单的数数的方法,先从最低位数,数到最大值时,进一位,低位置零,直到最高位也达到最大:
// radix[i] 表示第 i 位的进制 // digits 数组是输出,长度也是 len // 开始调用该函数时要将 digits 清零 bool next(const int* radix, int len, int* digits) { for (int i = len-1; i >= 0; --i) { assert(radix[i] > 0); assert(digits[i] < radix[i]); if (++digits[i] == radix[i]) digits[i] = 0; else return true; // 下一个数字 } return false; // 已结束,数字数完了 }
当然这个问题还有别的解法,例如用直接的树的深度优先遍历法。不过 next 函数本质上就是一个树深度优先遍历的 iterator。每次得到一个digits,就可以拼接字符串了:
str = ""; for (int i = 0; i < len; ++i) str += x[i][digits[i]];
对正则表达式熟悉的朋友可能一开始就想到了:"(x11|x12|x13...)(x21|x22|x23...)(x31|x32|x33...)..."
这个方法比hash table 好一点,现实中这种办法用起来也很快捷。但是,如果$x1 有 1,000,000 种可能,$2 到 $6 都只有 1 种可能,现存的任何一个正则表达式引擎都会爆掉!此时这种办法反而远比 hash table 要差。