Miff의 대답은 확실히 우아합니다. 어쨌든 나는 거의 끝났으므로 그것을 제공합니다. 좋은 점은 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 unpaired 문자 중 하나를 페어링해야하기 때문에 쉽습니다. 따라서 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는 그 세부 정보로 인해 조금 더 어색합니다. 그러나 스택이 500 또는 1000 중첩 수준에서 오버플로 될 때 적어도 Java에서는 재귀를 사용하지 않기 때문에 재귀를 사용하지 않는 동기가있었습니다.
n = 500, d = 4 및 u = 0의 결과는 다음과 같습니다.
N (500)
중간 결과 기억으로 인해 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);
}
}