給定字符串,可刪除任意字符(也可不刪),求有多少種方式使剩餘字符形成回文串。空串不算回文。結果可能很大,需用 64 位整數。
dp[i][j] = 子串 s[i..j] 中非空回文子序列的個數(i ≤ j)。
s[i..j] 的回文子序列 = s[i+1..j] 的 ∪ s[i..j-1] 的,但 s[i+1..j-1] 被重複計算了兩次:
dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]
除了上述並集,還有新增回文:
新增數量 = dp[i+1][j-1] + 1,故:
dp[i][j] = dp[i+1][j] + dp[i][j-1] + 1
(注意:此時 dp[i+1][j] + dp[i][j-1] 已包含 dp[i+1][j-1] 兩次,加 1 後恰好抵消減法)
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int MAXN = 100;
char s[MAXN];
ll dp[MAXN][MAXN];
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%s", s);
int len = strlen(s);
// 初始化:長度 1
for (int i = 0; i < len; ++i) dp[i][i] = 1;
// 區間 DP:長度從 2 到 len
for (int L = 2; L <= len; ++L) {
for (int i = 0; i + L <= len; ++i) {
int j = i + L - 1;
if (s[i] == s[j])
dp[i][j] = dp[i+1][j] + dp[i][j-1] + 1;
else
dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1];
}
}
printf("%lld\n", dp[0][len-1]);
}
return 0;
}
dp[i][i] = 1(單字符是回文)long longdp[i][j] = max(...)dp[i][j] = min(...)