【算法题解】河南师范大学 “青创杯” G : 回文子序列计数

【算法题解】回文子序列计数

题目

image-20221028214659359

思路

我们这样想,我们需要求以某个位置i为中心构成的回文串有多少种可能,那么我们可以将这个字符串S以x作为分割点,将整个字符串一分为三分,第一份是1 ~ i-1中某个删除方案中剩下的子序列构成的字符串L,第二份就是i,第三份就是i+1 ~ n中某个删除方案下剩下的子序列构成的字符串R。现在我们的问题就转变成了有多少种方案可以使L i R 形成一个回文串,并且LR两个字符串的长度相等。

image-20221028220147618

下面我们利用闫氏DP大法

状态表示f[i][j]表示1 ~ i 形成的部分和j ~ n形成的部分构成的回文子序列数,注意这里i < j1 ~ i中保留的字符数目和j ~ n中保留的字符数目相等。

状态计算

f[i][j] = f[i][j + 1] + f[i - 1][j] - f[i-1][j+1]

  • ij: f[i][j + 1]
  • ji: f[i - 1][j]

为啥这里要减去f[i-1][j+1]呢,因为用到了容斥原理f[i][j+1] + f[i-1][j]多算了f[i-1][j+1]这部分。为什么呢?

假设不减会是什么情况

  • f[i][j + 1] = f[i][j + 2] + f[i - 1][j + 1]
  • f[i - 1][j] = f[i - 1][j + 1] + f[i - 2][j]

两个都包含f[i - 1][j + 1],那就相当于加了两次,所以我们要减去。

上面的情况都是不同时包含i 和 j的,也就是说i 和 j 同时存在时还没有做出任何贡献

  • s[i] != s[j] :这就不需要计算了,因为同时加上s[i]、s[j]就构不成回文串了

  • s[i] == s[j] f[i][j] += f[i - 1][j + 1] + 1

为什么这样写呢:此时s[i] == s[j],那么s[i]s[j]肯定就构成了一个回文串,我们就把答案+1

我们再严谨的思考一下,对于区间1 ~ i-1j+1 ~ n构成的回文子序列是不是加上s[i] s[j]后也是一个新的回文子序列呢,因为它俩相等,答案是一定的,所以还需要把答案再加上f[i-1][j+1]

image-20221029114902583

我们计算完f[i][j]之后,怎么算x[i]呢,很简单,根据实际含义来看,x[i] = f[i - 1][i + 1]

最后处理一下结果即可,别忘了取模。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, inf = 0x3f3f3f3f, mod = 1e9 + 7;
long long f[3010][3010];
char s[3030];
void solve()
{
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for (int i = 1; i <= n; i++)
    {
        for (int j = n; j > i; j--)
        {
            f[i][j] = f[i - 1][j] + f[i][j + 1] - f[i - 1][j + 1];
            if (s[i] == s[j])
                f[i][j] += f[i - 1][j + 1] + 1;
            f[i][j] %= mod;
        }
    }
    long long ans = 0;
    for (int i = 1; i <= n; i++)
    {
        ans ^= (i * (f[i - 1][i + 1] + 1) % mod + mod) % mod;
    }
    cout << ans;
}
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    int T = 1;
    while (T--)
        solve();
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值