1066 字
5 分钟
AtCoder ABC456_D: D - Not Adjacent 2 Editorial
AtCoder
2026-05-06

题目链接#

https://atcoder.jp/contests/abc456/tasks/abc456_d

题目大意#

题目大意#

给定一个只由 abc 组成的字符串 S

要求统计 S 的所有非空子序列中,满足相邻字符不相同的子序列数量,并对 998244353 取模。

注意:即使两个子序列对应的字符串相同,只要选取的位置不同,也算作不同的子序列。

解题思路#

解题思路#

本题要求统计字符串 S 的所有非空子序列中,满足相邻字符不相同的子序列数量。

由于字符串只包含 abc 三种字符,因此可以按照子序列的结尾字符来设计状态。

1. 状态设计#

设:

dp[i][0]:前 i 个字符中,以 a 结尾的合法非空子序列数量
dp[i][1]:前 i 个字符中,以 b 结尾的合法非空子序列数量
dp[i][2]:前 i 个字符中,以 c 结尾的合法非空子序列数量

其中,合法子序列指的是相邻两个字符不相同。

把字符映射成编号:

a -> 0
b -> 1
c -> 2

假设当前处理到第 i 个字符,它对应的编号为 u

2. 状态转移#

对于不是当前字符的结尾状态,也就是 j != u,当前字符不能让这些状态产生新的结尾,因此直接继承上一轮结果:

dp[i][j] = dp[i - 1][j] (j != u)

对于当前字符 u,需要考虑两种情况。

第一种情况是不选择当前字符。

那么前面已经存在的、以 u 结尾的合法子序列仍然保留:

dp[i - 1][u]

第二种情况是选择当前字符作为新的结尾。

由于题目要求相邻字符不能相同,所以当前字符 u 不能接在以 u 结尾的子序列后面,只能接在以其他字符结尾的合法子序列后面:

sum(dp[i - 1][v]) (v != u)

此外,当前字符还可以单独构成一个长度为 1 的子序列:

1

所以有:

dp[i][u] = dp[i - 1][u] + sum(dp[i - 1][v]) + 1 (v != u)

由于字符只有三种,所以这个式子也可以写成:

dp[i][u] = dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + 1

这里要注意,虽然式子中包含 dp[i - 1][u],但它表示的是“不选择当前字符时,保留之前以 u 结尾的子序列”,并不是把当前字符接到这些子序列后面。

3. 为什么可以省略第一维#

从状态转移可以发现,计算第 i 轮状态时,只会用到第 i - 1 轮的三个状态:

dp[i - 1][0]
dp[i - 1][1]
dp[i - 1][2]

不会用到更早的状态。

因此,二维数组:

dp[i][3]

可以优化成一维数组:

dp[3]

其中:

dp[0] 表示当前已经处理过的字符中,以 a 结尾的合法非空子序列数量
dp[1] 表示当前已经处理过的字符中,以 b 结尾的合法非空子序列数量
dp[2] 表示当前已经处理过的字符中,以 c 结尾的合法非空子序列数量

遍历到当前字符 u 时,只需要更新:

dp[u] = dp[0] + dp[1] + dp[2] + 1;

其他两个状态不变,相当于自动完成了:

dp[i][j] = dp[i - 1][j] (j != u)

因此程序中可以省略第一维,实现滚动数组优化。

4. 最终答案#

处理完整个字符串后,所有合法非空子序列一定分别以 abc 中的某一个字符结尾。

所以答案为:

ans = dp[0] + dp[1] + dp[2]

由于答案可能很大,需要对 998244353 取模。

5. 复杂度分析#

字符串只需要遍历一遍,每个字符的转移都是常数时间。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码实现#

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=3e5+10;
ll dp[3];
//dp[N][3]:前i个字符, 以a, b, c为结尾的子序列个数有多少
string s;
int main(){
cin>>s;
for(char c:s){
int u;
if(c=='a') u=0;
if(c=='b') u=1;
if(c=='c') u=2;
dp[u]=(dp[0]+dp[1]+dp[2]+1+mod)%mod;
//不等于u的, 保留上一轮的结果
//u=a时, dp[i][a]=dp[i-1][a](不能选)+dp[i-1][b]+dp[i-1][c](可以选)+1(单独字符)
//类似于01背包, 第一维可省略
}
ll res=0;
for(int i=0;i<3;i++) res=(res+dp[i]+mod)%mod;
printf("%lld\n", res);
return 0;
}
AtCoder ABC456_D: D - Not Adjacent 2 Editorial
https://shenyize.com/posts/atcoder/abc456_d/
作者
Shenyize
发布于
2026-05-06
许可协议
CC BY-NC-SA 4.0