題意
給定一個字串 (),求有多少個字串 使得 為 的子序列。
解析
這題的難點主要在於防止算重。我們定義 表示前一個 中的字元在原串中出現的位置, 表示後一個 中的字元在原串中出現的位置,令 表示從 往後第一個 出現的位置(包括 )。
為了避免算重,我們強制要求 和 儘量小。舉個例子,如果原串是 acbcab,
是 ab,那麼 和 便如下計算:
可以發現,這樣算出來的 和 一定是最小的,並且對於每一個 ,最多隻有一個 和 。
我們列舉一個 ,則 。令 表示 的末尾元素為 , 的末尾元素為 時的方案數,顯然 。轉移方程:。當然,人人為我的轉移寫起來很困難,可以考慮我為人人的轉移,列舉一個字元 , 會對 貢獻。最後統計 的答案就 。
時間複雜度 。
#include <algorithm>
#include <array>
#include <iostream>
#include <string>
#include <vector>
#include <atcoder/modint>
using mint = atcoder::modint998244353;
int main()
{
std::string s;
std::cin >> s;
int n = s.size();
std::vector<std::array<int, 26>> next(n + 1);
std::fill(next[n].begin(), next[n].end(), n);
for (int i = n; i > 0; ){
i--;
next[i] = next[i + 1];
next[i][s[i] - 'a'] = i;
}
mint ans = 0;
for (int q = 0; q < n; q++) {
int p = next[0][s[q] - 'a'];
if (p >= q) continue;
std::vector f(n, std::vector<mint>(n));
f[p][q] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int ch = 0; ch < 26; ch++) {
int ni = next[i + 1][ch];
int nj = next[j + 1][ch];
if (ni >= q || nj >= n) continue;
f[ni][nj] += f[i][j];
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (next[i + 1][s[q] - 'a'] != q) continue;
ans += f[i][j];
}
}
}
std::cout << ans.val() << std::endl;
}