Nun Heh Heh Aaaaaaaaaaa 字符串,dp

该博客主要探讨了一种字符串子序列问题,即计算一个特定子序列(numhehheh)加上任意数量的a的子序列总数,并对结果取模998244353。博主通过动态规划(dp)方法进行预处理,计算每个位置后续可能出现a的个数,然后递归计算以目标子序列中每个字符结尾的子序列数。最终,博主给出了C++代码实现,展示了如何计算所有可能的组合并输出结果。
摘要由CSDN通过智能技术生成

在这里插入图片描述

题意 :

  • 给一字符串,求其有多少个子序列为𝚗𝚞𝚗𝚑𝚎𝚑𝚑𝚎𝚑加上若干个a(大于等于1),最后%998244353

思路 :

  • 由非连续子序列联想到dp
  • 预处理,cnta数组代表第i个后面出现的字母a的个数
  • 对于第i个后面的所有的a每一个选与不选一共有 2 n 2^n 2n种情况,因为不能是空集,所以是 2 n − 1 2^n-1 2n1种情况
  • f [ i , j ] f[i, j] f[i,j]表示前i个字符中以numhehheh中第j个结尾的个数
  • 将它们相乘就是位置i的贡献,再从前往后遍历相加每一个点的贡献就是总的方案数
#include <iostream>
#include <cstring>

using namespace std;

typedef long long ll;

const ll mod = 998244353;
const int N = 1e5 + 10;

ll cnta[N], f[N][10];

ll qmi(ll a, ll b, ll p)
{
    ll res = 1 % p;
    while (b)
    {
        if (b & 1) res = res * a % p;
        a = a * (ll)a % p;
        b >>= 1;
    }
    
    return res;
}

int main()
{
    int T;
    cin >> T;
    
    while (T -- )
    {
        string s;
        cin >> s;
        s = " " + s;
        
        // 多组输入
        memset(f, 0, sizeof f);
        memset(cnta, 0, sizeof cnta);
        
        // 预处理,i及之后出现a的个数
        for (int i = s.size(); i; i -- ) cnta[i] = cnta[i + 1] + (s[i] == 'a');
        
        ll res = 0;
        
        for (int i = 1; i <= s.size(); i ++ )
        {
            f[i][1] = (f[i - 1][1] + (s[i] == 'n')) % mod;
            f[i][2] = (f[i - 1][2] + f[i - 1][1] * (s[i] == 'u')) % mod;
            f[i][3] = (f[i - 1][3] + f[i - 1][2] * (s[i] == 'n')) % mod;
            f[i][4] = (f[i - 1][4] + f[i - 1][3] * (s[i] == 'h')) % mod;
            f[i][5] = (f[i - 1][5] + f[i - 1][4] * (s[i] == 'e')) % mod;
            f[i][6] = (f[i - 1][6] + f[i - 1][5] * (s[i] == 'h')) % mod;
            f[i][7] = (f[i - 1][7] + f[i - 1][6] * (s[i] == 'h')) % mod;
            f[i][8] = (f[i - 1][8] + f[i - 1][7] * (s[i] == 'e')) % mod;
            
            // 轮到匹配串中的最后一个位置时,与总方案res直接相关,就不用加上f[i - 1][9],因为循环在i - 1的时候,已经加到res中去了,现在不需要加,否则会重复
            f[i][9] = (f[i - 1][8] * (s[i] == 'h')) % mod;
            
            if (f[i][9]) res = (res + f[i][9] * (qmi(2, cnta[i + 1], mod) - 1) % mod) % mod;
        }
        
        cout << res << endl;
    }
    
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值