题目描述

给定一个长度为 $n$ 的,仅包含首 $m$ 个小写字母的字符串 $S$。

计算存在多少个不同的长度为 $n$ 的,只包含首 $m$ 个字母的字符串 $T$,满足 $S$ 和 $T$ 的最长公共子序列 (LCS) 的长度是 $n-1$。

输入格式

第一行包含两个正整数 $n, m$ ($n \leq 10^5, 2 \leq m \leq 26$),表示 $S, T$ 的长度和字符集的大小。

接下来一行包含字符串 $S$。

输出格式

输出一行一个整数,表示满足条件的 $T$ 的个数。

题解

这道题方法很多。下面将一种基于 DP 的做法。

我们知道,根据经典的 $O(n^2)$ DP,LCS 的转移是根据对应位置上的字符是否相同分两种情况转移的。即 $f_{i, j}$ 只与 $f_{i-1, j}, f_{i, j-1}$ 以及 $f_{i-1, j-1}$ 有关。

但是我们有 $f_{n, n} = n-1$,这样以来,对于所有满足 $|i-j| \leq 1$ 的 $(i, j)$,均有 $\min \{i, j\} - 1 \leq f_{i, j} \leq \min \{i, j\}$。

因此,我们只关心对于 $|i-j| \leq 1$ 的 $(i, j)$,即 $f_{i, i-1}, f_{i, i}, f_{i, i+1}$,由于每个 DP 值只有两种情况,因此一共至多只有 $8$ 种情况,我们可以将它状压为状态 $A$。(注:$f_{i, j}$ 表示 $T$ 的 $i-$前缀和 $S$ 的 $i-$前缀的 LCS 的长度)。

记 $g_{i, A}$ 表示有多少个 $T$ 的 $i-$前缀,满足上述三个 DP 值的状态为 $A$,很容易预处理出初始状态。考虑转移,即 $T$ 中加入第 $i+1$ 个字符串。那么,显然枚举 $m$ 个字符,然后分别和 $s_i, s_{i+1}, s_{i+2}$ (如果 $s$ 以 $1$ 编号) 比较,然后根据上述两种方式转移,得到新的状态 $A'$,即可向 $g_{i+1, A'}$ 转移。

最后由于只要求 $f_{n, n} = n-1$,而其它的我们无需关心 (即怎么样都可以),因此把满足条件的 $4$ 种状态的总数加起来就可以了。(这题其实不用取模,因为实际的答案不会很大)

代码

#include <bits/stdc++.h>
#define N 100034
using namespace std;

typedef char state;
typedef long long ll;

int n, m;
char s[N];
ll f[N][8];

int main(){
	int i, j, k; state p;
	int q[3], nq[3];
	scanf("%d%d%s", &n, &m, s + 1);
	for(i = 1; i <= n; ++i) s[i] &= 31;
	for(i = 1; i <= m; ++i){
		p = 1; // lcs[1][0] = 1
		if(i == s[1]) p |= 2; // lcs[1][1] = 1
		if(i == s[1] || i == s[2]) p |= 4; // lcs[1][2] = 1;
		++f[1][p];
	}
	for(i = 1; i < n; ++i)
		for(j = 0; j < 8; ++j)
			if(f[i][j]){
				q[0] = i - 2 + (j & 1);
				q[1] = i - 1 + (j >> 1 & 1);
				q[2] = i - 1 + (j >> 2 & 1);
				for(k = 1; k <= m; ++k){
					nq[0] = (k == s[i] ? q[0] + 1 : q[1]);
					nq[1] = (k == s[i + 1] ? q[1] + 1 : max(nq[0], q[2]));
					nq[2] = (k == s[i + 2] ? q[2] + 1 : nq[1]);
					if(nq[0] < i - 1 || nq[1] < i || nq[2] < i) continue;
					p = nq[0] - (i - 1);
					p |= (nq[1] - i) << 1;
					p |= (nq[2] - i) << 2;
					f[i + 1][p] += f[i][j];
				}
			}
	printf("%lld\n", f[n][0] + f[n][1] + f[n][4] + f[n][5]);
	return 0;
}

坑1:注意 LCS 的转移方式,对应位置上字符不相等时,应该是 $f_{i, j} = \max \{f_{i-1, j}, f_{i, j-1}\}$。