学习笔记:2021CCPC网络赛(重赛)F. Nun Heh Heh Aaaaaaaaaaa
原题目:
Problem Description:
Vasily Tadokorov is a stringologist. He thinks a string is fragrant if it can be divided into two parts — nunhehheh as the prefix and a number of (excluding 0) a as the suffix. For example, nunhehhehaaaaaa is fragrant, but nunhehheh and nunhehhehoooaaa are not fragrant.
Today Vasily Tadokorov has some strings consisting of lowercase English letters. For each string, he wants to know how many subsequences of this string are fragrant. A string a is a subsequence of a string b if a can be obtained from b by deletion of several (including 0) characters.
Input
The first line contains an integer T (1≤ T ≤1000), denoting the number of strings.
Each of the next T lines contains a string S (1≤ |S| ≤ 105) consisting of lowercase English letters.
The total length of the strings in the input will not exceed 106
Output
For each of the given T strings, output the answer modulo 998244353.
Sample Input
2 nunhehhehahaahahahahahahaahaahahahahha nunhehhehhehhahaahahahaahaahahaaaahaa
Sample Output
114514 1919810
Source
2021中国大学生程序设计竞赛(CCPC)- 网络选拔赛(重赛)
题意:
给定一个串 S,求出 S 中包含前缀为nunhehheh,后缀为一个或多个a的子序列的个数,如:nunhehhehaaaaaa 满足题意,nunhehheh 和 nunhehhehoooaaa 不满足题意。
解析:
这道题我先自己动手算了一下第一个输入样例 nunhehhehahaahahahahahahaahaahahahahha 的值,其中,前缀可以为: nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha nunhehhehahaahahahahahahaahaahahahahha
后缀分别有16,15,13,12,11,10,9,8,6,4,3,2,1,1个 a。 由排列组合的知识可知:n个a有
f
(
n
)
=
C
n
1
+
C
n
2
+
.
.
.
+
C
n
n
=
2
n
?
1
f(n)=C_{n}^{1}+C_{n}^{2}+...+C_{n}^{n}=2^n-1
f(n)=Cn1?+Cn2?+...+Cnn?=2n?1个子序列。所以共有
f
(
16
)
+
f
(
15
)
+
f
(
13
)
+
f
(
12
)
+
f
(
11
)
+
f
(
10
)
+
f
(
9
)
+
f
(
8
)
+
f
(
6
)
+
f
(
4
)
+
f
(
3
)
+
f
(
2
)
+
f
(
1
)
+
f
(
1
)
=
114514
f(16)+f(15)+f(13)+f(12)+f(11)+f(10)+f(9)+f(8)+f(6)+f(4)+f(3)+f(2)+f(1)+f(1)=114514
f(16)+f(15)+f(13)+f(12)+f(11)+f(10)+f(9)+f(8)+f(6)+f(4)+f(3)+f(2)+f(1)+f(1)=114514 满足题意。
根据这个自前向后推的思路,又因为前缀和输入的都是字符串,我随即想到了动态规划(DP)算法。输入的串为 S,令前缀为T=“nunhehheh”。由于T是固定的,所以可以以T作为基准,令
g
[
i
]
[
j
]
g[i][j]
g[i][j]为S中前
i
i
i个字符串中的子序列与T中前
j
j
j个字符串匹配的个数,可求得DP转移方程为:
●
?
g
[
i
]
[
0
]
=
1
(
边
界
)
●\ g[i][0] = 1 (边界)
●?g[i][0]=1(边界)
●
?
g
[
i
]
[
j
]
=
g
[
i
?
1
]
[
j
?
1
]
+
g
[
i
?
1
]
[
j
]
?
(
i
f
?
S
[
i
]
=
=
T
[
j
]
)
●\ g[i][j] = g[i-1][j-1] + g[i-1][j] \ (if\ S[i]==T[j])
●?g[i][j]=g[i?1][j?1]+g[i?1][j]?(if?S[i]==T[j]) (当S中前
i
?
1
i-1
i?1个字符串中的子序列与T中前
j
?
1
j-1
j?1个字符串匹配时,S中前
i
i
i个字符串中的子序列与T中前
j
j
j个字符串一定匹配)
●
?
g
[
i
]
[
j
]
=
g
[
i
?
1
]
[
j
]
?
(
o
t
h
e
r
s
)
●\ g[i][j] = g[i-1][j] \ (others)
●?g[i][j]=g[i?1][j]?(others)
然后对于这道题而言,T=“nunhehheh” 的长度为9,输入的串S长度为
m
m
m,则分别求出
g
[
9
]
[
9
]
,
g
[
10
]
[
9
]
?
g
[
9
]
[
9
]
,
.
.
.
,
g
[
m
]
[
9
]
?
g
[
m
?
1
]
[
9
]
g[9][9],g[10][9]-g[9][9],...,g[m][9]-g[m-1][9]
g[9][9],g[10][9]?g[9][9],...,g[m][9]?g[m?1][9](因为前缀最后一位必须为h)与其后缀中a的个数,相乘即可。最后记得取模。
代码:
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const int M = 15;
const ll mod = 998244353;
ll g[N][M];
ll num[N];
ll mod_pow(ll n)
{
ll ans = 1, a = (ll)2;
while (n){
if (n & 1) {
ans = ans * a % mod;
}
a = a * a % mod;
n >>= 1;
}
return ans % mod;
}
int main(){
int T;
cin >> T;
while (T > 0){
string s, t = "nunhehheh";
memset(g, 0, sizeof(g));
memset(num, 0, sizeof(num));
cin >> s;
int n = s.length(), m = t.length();
s = " " + s, t = " " + t;
for (int i = n; i >= 1; i--){
if (s[i] == 'a') {
num[i] = num[i + 1] + 1;
}
else {
num[i] = num[i + 1];
}
}
for (int i = 0; i <= n; i++) {
g[i][0] = 1;
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (j > i) {
continue;
}
if (s[i] == t[j]) {
g[i][j] = (g[i - 1][j - 1] + g[i - 1][j]) % mod;
}
else {
g[i][j] = g[i - 1][j] % mod;
}
}
}
ll res = 0;
ll lastVal = 0;
for (int i = 1; i < n; i++){
if (s[i] == 'h'){
res += (((g[i][m] - lastVal + mod) % mod * (mod_pow(num[i + 1]) - 1)) % mod) % mod;
lastVal = g[i][m];
}
}
cout << res % mod << endl;
T--;
}
return 0;
}
最后,在杭电oj上提交后可以通过。
|