思路
首先先考慮弱化版怎麼做,即如何求本質不同子序列數量。
不妨定義 \(dp_{i}\) 在前 \(i\) 位中選,且以 \(i\) 結尾的本質不同子序列數量。
顯然,有(其中 \(nxt_{i,c}\) 表示在 \(i\) 之後,第一次出現 \(c\) 的位置):
\[
dp_{i} \rightarrow dp_{nxt_{i,c}}
\]
本題中的 \(TT\) 是由兩個 \(T\) 拼接而成,所以考慮列舉兩個 \(T\) 的起始位置 \(i,j\)。
在這裡定義 \(dp_{i,j}\) 表示兩個 \(T\) 分別以 \(i,j\) 結尾的本質不同子序列數量。
再列舉一下兩個 \(T\) 倒數第二個字元的位置,記作 \(l,r\),並且列舉這個字元 \(c\)。
那麼,如果想使 \(T\) 繼續延伸,那麼,必須使新擴充套件的字元相同。因此,記 \(nl = nxt_{l,c},nr = nxt_{r,c}\)。
根據上述求本質不同子序列數量的狀態轉移方程,可得:
\[
dp_{nl,nr} = dp_{nl,nr} + dp_{l,r}
\]
在統計答案的時候,不能直接將所有 \(dp_{i,j}\) 加起來,這樣會導致重複計算。所以,我們令 \(T\) 取最前面的字元。
Code
#include <bits/stdc++.h>
#define int long long
#define re register
using namespace std;
const int N = 110,mod = 998244353;
int n,ans;
int nxt[N][N],dp[N][N];
string s;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s;
n = s.length();
s = ' ' + s;
for (re char c = 'a';c <= 'z';c++) nxt[n][c - 'a'] = n + 1;//預處理 nxt
for (re int i = n - 1;~i;i--){
for (re char c = 'a';c <= 'z';c++) nxt[i][c - 'a'] = nxt[i + 1][c - 'a'];
nxt[i][s[i + 1] - 'a'] = i + 1;
}
for (re int j = 2;j <= n;j++){
int i = nxt[0][s[j] - 'a'];
if (i >= j) continue;
memset(dp,0,sizeof(dp));
dp[i][j] = 1;//i,j 顯然合法,貢獻為 1
for (re int l = 1;l <= n;l++){
for (re int r = l + 1;r <= n;r++){
for (re char c = 'a';c <= 'z';c++){
int nl = nxt[l][c - 'a'];
int nr = nxt[r][c - 'a'];
if (nl >= r || nr > n) continue;
dp[nl][nr] = (dp[nl][nr] + dp[l][r]) % mod;
}
}
}
for (re int l = 1;l <= n;l++){
for (re int r = l + 1;r <= n;r++){
if (nxt[l][s[j] - 'a'] == j) ans = (ans + dp[l][r]) % mod;//要取最前面的
}
}
}
printf("%lld",ans);
return 0;
}