学习笔记:2021CCPC网络赛(重赛)F. Nun Heh Heh Aaaaaaaaaaa 题解

学习笔记: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 满足题意,nunhehhehnunhehhehoooaaa 不满足题意。

解析:

这道题我先自己动手算了一下第一个输入样例
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
由排列组合的知识可知:na 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=2n1个子序列。所以共有 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[i1][j1]+g[i1][j] (if S[i]==T[j])
(当S中前 i − 1 i-1 i1个字符串中的子序列与T中前 j − 1 j-1 j1个字符串匹配时,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[i1][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[m1][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]; //S中前i个字符串中的子序列与T中前j个字符串匹配的个数
ll num[N]; //从n->i a的个数

ll mod_pow(ll n) //使用快速幂运算求2^n % mod
{
    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; //上一次S序列中的子序列为T的个数
        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];
            } //g[i][m]-g[i-1][m]
            //由于g为取余后的值,g[i][m]可能小于g[i-1][m]。运用取模的同余性质可避免出现负值。
        }
        cout << res % mod << endl;
        T--;
    }
    return 0;
}

最后,在杭电oj上提交后可以通过。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值