米夫的答案肯定是优雅的。由于我的矿井几乎完工了,所以我仍然提供它。好消息是,对于n = 500,我得到相同的结果:-)
令d为允许的不同字符数,在您的情况下d = 4。
令n为字符串的长度,最终您将看到n的偶数。
令u为字符串中未配对字符的数量。
令N(n,d,u)为长度为n的字符串数,该字符串由d个不同的字符构成,并具有u个不成对的字符。让我们尝试计算N。
有很多特殊情况需要观察:
u> d或u> n => N = 0
u <0 => N = 0
n%2!= u%2 => N = 0。
当从n步进到n + 1时,u必须增加1或减少1,因此我们根据
N(n,d,u)= f(N(n-1,d,u-1),N(n-1,d,u + 1))
有多少种方法可以将u减一。这很容易,因为我们必须将u个不成对的字符之一配对,这使得它只是u。因此,f的第二部分将读取为(u + 1)* N(n-1,d,u + 1),但需要注意的是,如果u + 1> n-1或u,我们必须观察到N = 0 +1> d。
一旦我们理解了这一点,就很容易看出f的第一部分是什么:当存在u-1个不成对的字符时,可以通过多少种方式增加u。好吧,我们必须选择已配对的(k-(u-1))个字符之一。
因此,考虑到所有极端情况,N的递归公式为
N(n,d,u)=(d-(u-1))* N(n-1,d,u-1)+(u + 1)* N(n-1,d,u + 1)
我不会在http://en.wikipedia.org/wiki/Concrete_Mathematics中阅读如何解决递归问题。
相反,我写了一些Java代码。再次有点笨拙,反正Java的冗长性也是如此。但是我有动机不使用递归,因为递归最早发生到很早,至少在Java中,当堆栈在500或1000个嵌套级别上溢出时。
n = 500,d = 4和u = 0的结果是:
N(500,4,0)= 133938575898283415118553131132500226320175601463191700930468798546293881390617015311649797351961982265949334114694143353148393160391539255449807219683895854579576904278803546802604812520890471375776580516387245505699580957690958327953827520394225
由于记忆中间结果,因此可以在0.2秒内完成计算。N(40000,4,0)在不到5秒的时间内进行计算。代码也位于此处:http : //ideone.com/KvB5Jv
import java.math.BigInteger;
public class EvenPairedString2 {
private final int nChars; // d above, number of different chars to use
private int count = 0;
private Map<Task,BigInteger> memo = new HashMap<>();
public EvenPairedString2(int nChars) {
this.nChars = nChars;
}
/*+******************************************************************/
// encodes for a fixed d the task to compute N(strlen,d,unpaired).
private static class Task {
public final int strlen;
public final int unpaired;
Task(int strlen, int unpaired) {
this.strlen = strlen;
this.unpaired = unpaired;
}
@Override
public int hashCode() {
return strlen*117 ^ unpaired;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Task)) {
return false;
}
Task t2 = (Task)other;
return strlen==t2.strlen && unpaired==t2.unpaired;
}
@Override
public String toString() {
return "("+strlen+","+unpaired+")";
}
}
/*+******************************************************************/
// return corner case or memorized result or null
private BigInteger getMemoed(Task t) {
if (t.strlen==0 || t.unpaired<0 || t.unpaired>t.strlen || t.unpaired>nChars
|| t.strlen%2 != t.unpaired%2) {
return BigInteger.valueOf(0);
}
if (t.strlen==1) {
return BigInteger.valueOf(nChars);
}
return memo.get(t);
}
public int getCount() {
return count;
}
public BigInteger computeNDeep(Task t) {
List<Task> stack = new ArrayList<Task>();
BigInteger result = null;
stack.add(t);
while (stack.size()>0) {
count += 1;
t = stack.remove(stack.size()-1);
result = getMemoed(t);
if (result!=null) {
continue;
}
Task t1 = new Task(t.strlen-1, t.unpaired+1);
BigInteger r1 = getMemoed(t1);
Task t2 = new Task(t.strlen-1, t.unpaired-1);
BigInteger r2 = getMemoed(t2);
if (r1==null) {
stack.add(t);
stack.add(t1);
if (r2==null) {
stack.add(t2);
}
continue;
}
if (r2==null) {
stack.add(t);
stack.add(t2);
continue;
}
result = compute(t1.unpaired, r1, nChars-t2.unpaired, r2);
memo.put(t, result);
}
return result;
}
private BigInteger compute(int u1, BigInteger r1, int u2, BigInteger r2) {
r1 = r1.multiply(BigInteger.valueOf(u1));
r2 = r2.multiply(BigInteger.valueOf(u2));
return r1.add(r2);
}
public static void main(String[] argv) {
int strlen = Integer.parseInt(argv[0]);
int nChars = Integer.parseInt(argv[1]);
EvenPairedString2 eps = new EvenPairedString2(nChars);
BigInteger result = eps.computeNDeep(new Task(strlen, 0));
System.out.printf("%d: N(%d, %d, 0) = %d%n",
eps.getCount(), strlen, nChars,
result);
}
}