字符串数,每个字符必须出现偶数次


9

我已经为此问题problem了一段时间,这真的开始让我感到沮丧。问题是:

我有一个字符集,ABC,和D。我必须说出从这些字符中构建字符串的方式,长度是多少n,每个字符必须出现偶数次。

例如,答案为n = 24:

AA
BB
CC
DD

答案n = 4是40。其中一些有效的字符串是:

AAAA
AABB
CACA
DAAD
BCCB

我坚持提出一个逻辑。我觉得可能会有一个DP解决方案。解决这个问题的方法是蛮横的:解决方案的数量迅速增长到巨大数量。

我试图在纸上画出各种想法,但没有成功。我几乎不得不放弃所有这些想法,因为它们的复杂性太大。该解决方案应有效n = 10^4

我的想法之一是永远不要跟踪实际的字符串,而只能跟踪每个字符出现的是偶数还是奇数次。我无法提出一种应用此逻辑的方法。

谁能帮我?


3
您需要枚举字符串还是计算字符串数量?如果只需要字符串数,则可以使用组合运算符直接计算数量。

@Snowman仅需要可能的字符串数。但是,我发现我不太可能在这里使用组合器。即使有办法,我敢肯定问题不应该用纯数学来解决,因此不希望这样做。还是什么意思?
Olavi Mustanoja'2

2
当然可以使用组合符号。对于长度为N的字符串,请获取{AA,BB,CC,DD}的所有组合。对于每种组合,获取唯一的排列。然后将每种组合的结果合并为一组唯一的排列。我不确定如何执行此操作,主要是因为唯一性约束,但是我确定有办法。

@雪人,我明白你的意思。但这是否至少需要存储组合?获取唯一排列的数量需要这样做,并且组合的数量会迅速增长到我无法存储的比例。
Olavi Mustanoja'2

1
可能吧 我对组合语言不太了解,无法确定。也许Mathematics.SE有与此类似的问题?我现在没有时间去研究它,但这是一个有趣的问题。我会考虑一下,然后再检查。

Answers:


5

设置f(n,d)n使用d不同字符(即d=4您的情况)给出(偶数)个长度的排列数目的函数。

显然f(0,d) = 1f(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

对于您感兴趣的数字种类,我以为将数字存储在二维数组中并迭代d会更有效,但是这可能取决于您选择的语言。


4

米夫的答案肯定是优雅的。由于我的矿井几乎完工了,所以我仍然提供它。好消息是,对于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); 
  }
}

2

我试图提出一个解决方案,但失败了,并在Mathematics.StackExchange上问了同样的问题。感谢Rus May,这是Common Lisp中的解决方案:

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

对于的奇数值,该值始终返回0 n。对于n = 500,这是SBCL的输出:

* (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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.