1 条题解
-
0
自动搬运
来自洛谷,原作者为

NZSWW33OMF2GC
**搬运于
2025-08-24 21:53:26,当前版本为作者最后更新于2020-05-11 10:50:17,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
For better reading experience, click here.
之前的几篇题解用到了‘重新统计’这样的步骤,思维量较大(萌新我现在也没太搞懂),于是我自己鼓捣了一下,提出一种比较简单的思路。这篇题解给出完整的论证过程和转移方程,给各位作为参考。
求三个字符串中互不相同的公共子串数目。
三个字符串分别记为
a、b、c,下标均起始于 1。对三个字符串中的每一个字符,计算它之前一次出现的位置,存入对应的last数组中(若该字符之前未出现过则存入 0)。f[i][j][k]代表a[1..i]、b[1..j]和c[1..k]上互不相同的公共子串数目(即子问题的解)。下面动态规划计算f。当
a[i] == b[j] == c[k] == ch时,会产生新的公共子串。此时考虑将ch接到之前出现的所有公共子串之后,这样就产生了与之前数量相同的新公共子串,再加上ch本身也是新公共子串,故此时有但是上面没有考虑重复的情况。哪一部分公共子串是重复的呢?为了方便,我们先将
$$f_{i,\,j,\,k} = 2\cdot f_{i-1,\,j-1,\,k-1} - f_{li-1,\,lj-1,\,lk-1}. $$ch在三个字符串中上一次出现的位置lasta[a[i]]、lastb[b[j]]和lastc[c[k]]分别记为li、lj、lk。那么在计算f[li][lj][lk]和f[i][j][k]时均会在f[li-1][lj-1][lk-1]所代表的公共子串末尾接上ch,这部分就是重复的。还有ch本身亦重复,故当li、lj和lk均不为 0 时,需要考虑重复数。若
$$\begin{aligned} f_{i,\,j,\,k} = &\phantom{+} f_{i-1,\,j,\,k} + f_{i,\,j-1,\,k} + f_{i,\,j,\,k-1}\\ &- f_{i-1,\,j-1,\,k} - f_{i,\,j-1,\,k-1} - f_{i-1,\,j,\,k-1}\\ &+ f_{i-1,\,j-1,\,k-1}. \end{aligned} $$a[i]、b[j]、c[k]不相等,那么没有新的公共子串产生,此时直接将之前的值继承过来即可。但问题在于,如何定义“之前”呢?直接复制f[i-1][j-1][k-1]显然是错的,因为未考虑在f[i-1][j][k]等处产生的新公共子串。很容易可以想到容斥原理:
下面附上核心代码:
for(int i = 1; i <= alen; ++i) for(int j = 1; j <= blen; ++j) for(int k = 1; k <= clen; ++k) if (a[i] == b[j] && b[j] == c[k]) { f[i][j][k] = f[i-1][j-1][k-1]*2 + 1; if (lasta[i] && lastb[j] && lastc[k]) f[i][j][k] -= f[lasta[i]-1][lastb[j]-1][lastc[k]-1] + 1; } else { f[i][j][k] = f[i-1][j][k] + f[i][j-1][k] + f[i][j][k-1] - f[i-1][j-1][k] - f[i][j-1][k-1] - f[i-1][j][k-1] + f[i-1][j-1][k-1]; }
我尽量在每一篇题解中做到语言通俗、思路清晰、逻辑严密。如果您从中得到了启发的话,一个小小的赞是对我最大的肯定!
- 1
信息
- ID
- 2793
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者