각 문자가 짝수 번 발생해야하는 문자열 수


9

나는이 문제에 대해 얼마 동안 내 두개골을 강타하고 있었고, 실제로 저를 좌절시키기 시작했습니다. 문제는:

나는 문자 집합을, A, B, C,와 D. 길이가 길고 n각 문자가 짝수 번 발생 해야하는 경우 해당 문자로 문자열을 작성할 수있는 방법을 알려야 합니다.

예를 들어 대답 n = 2은 4입니다.

AA
BB
CC
DD

n = 4은 40입니다. 유효한 문자열 중 일부는 다음과 같습니다.

AAAA
AABB
CACA
DAAD
BCCB

나는 논리를 생각해 냈습니다. DP 솔루션이있을 수 있다고 생각합니다. 이것을 통해 나의 길을 무차별 대입하는 것은 의문의 여지가 없습니다. 솔루션의 수가 급속히 증가하고 있습니다.

나는 종이에 모든 종류의 아이디어를 그리려고 애썼다. 복잡성이 너무 커서 거의 모든 아이디어를 버려야했습니다. 솔루션은 효율적이어야합니다 n = 10^4.

내 아이디어 중 하나는 실제 문자열을 추적하지 않고 각 문자가 짝수 또는 홀수 번 나타나는지 여부를 추적하는 것이 었습니다. 이 논리를 적용하는 방법을 생각해 낼 수 없었습니다.

누구든지 나를 도울 수 있습니까?


3
문자열을 열거하거나 문자열 수를 계산해야합니까? 당신은 단지 문자열의 수를 필요로하는 경우, 당신은 가능성이 사용할 수 조합론을 직접 양을 계산합니다.

@Snowman 가능한 문자열 수만 필요합니다. 그러나 여기에서 조합을 사용할 수는 없습니다. 방법이 있더라도 문제가 순수한 수학으로 해결 되지 않아야 한다고 확신하므로 그 이유는 원하지 않습니다. 아니면 무슨 뜻입니까?
Olavi Mustanoja

2
물론 당신은 조합을 사용할 수 있습니다. 길이가 N 인 문자열의 경우 {AA, BB, CC, DD}의 모든 조합을 가져옵니다. 각 조합에 대해 고유 순열을 구하십시오. 그런 다음 각 조합의 결과를 하나의 고유 순열 세트로 결합하십시오. 나는 주로 독창성 제약 때문에이 작업을 수행하는 방법을 모르겠지만 방법이 있다고 확신합니다.

@Snowman 무슨 말인지 알 겠어요. 그러나 적어도 조합을 저장해야하지 않습니까? 고유 순열 수를 얻으려면 이것이 필요하며 조합 수는 저장할 수없는 비율로 매우 빠르게 증가합니다.
Olavi Mustanoja

1
혹시. 나는 확실하게 알 수있는 조합론에 정통하지 않습니다. 아마 Mathematics.SE 와 비슷한 질문이 있습니까? 나는 지금 그것을 파헤칠 시간이 없지만 이것은 흥미로운 문제이다. 나는 그것에 대해 생각하고 다시 확인합니다.

답변:


5

고유 한 문자 (예 : 경우)를 사용하여 f(n,d)(짝수) 길이의 순열 수를 제공하는 함수로 설정하십시오 .ndd=4

분명 f(0,d) = 1하고 f(n,1) = 1당신은 단지 하나의 문자, 또는 제로 공백이 때와 같이 하나의 배열이있다.

이제 유도 단계 :

d문자를 사용하여 유효한 문자열을 만들려면 문자를 사용하여 더 짧은 짝수 길이의 문자열을 가져 와서이 d-1새 문자의 배수를 추가하여 최대 길이로 만드십시오. 배열의 수는 전체 문자열 길이 중에서 새 숫자를 갖도록 장소 choose(n,n_newdigits)를 선택할 수 n_newdigit있고 나머지는 원래 문자열로 순서대로 채워 지기 때문 입니다.

R에서 순진한 재귀를 사용하여 이것을 구현하기 위해 다음을 수행했습니다.

f <- function(n,d)
{
  if(n==0) return(1)
  if(d==1) return(1)
  retval=0
  for (i in seq(from=0, to=n, by=2)) retval=retval+f(n-i,d-1)*choose(n,i)
  return(retval)
}

f(4,4)
# 40    

f(500,4)
# 1.339386e+300 takes about 10 secs

관심있는 숫자의 종류에 대해 2 차원 배열에 숫자를 저장하고 d를 반복하여 반복하는 것이 더 효율적이라고 생각했지만 언어 선택에 따라 달라질 수 있습니다.


4

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); 
  }
}

2

나는 해결책을 찾으려고했지만 실패하고 Mathematics.StackExchange 에 대해 같은 질문을했습니다 . Rus May 덕분 에 Common Lisp의 솔루션이 있습니다.

(defun solve (n)
  (if (evenp n)
      (/ (+ (expt 4 n) (* 4 (expt 2 n))) 8)
      0))

홀수 값의 경우 항상 0을 반환합니다 n. 의 경우 SBCL을n = 500 사용한 출력은 다음과 같습니다.

* (time (solve 500))

    Evaluation took:
      0.000 seconds of real time
      0.000000 seconds of total run time (0.000000 user, 0.000000 system)
      100.00% CPU
      51,100 processor cycles
      0 bytes consed

    1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.