大意不再赘述。
思路:有了上一道回文串的题为基础,这一道题便很好思考了。由于只有删除操作,比较方便,所以我们用d[i][j]表示区间i~j内回文串的个数,初始化d[i][i] = 1,因为单个字符一定是回文串,而我们开始时,则需要从两个端点开始推。
基于删除操作的转移方程:
d[i][j] = d[i+1][j] + d[i][j-1] + 1;(str[i] == str[j]) ①
d[i][j] = d[i+1][j] + d[i][j-1] + d[i+1][j-1];(str[i] != str[j]) ②
①很好理解,一直递推下去,然后由于中间的子串可能为空,则在删除的操作上+1。②要减去一段重复的这一点我倒是没想出来,后来把图画出来发现果真如此,重复的就是一个串的子串,d[i+1][j-1]。
还有这题超INT,需用long long .
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int MAXN = 1010;
typedef long long LL;
char str[65];
LL d[70][70];
bool vis[70][70];
void init()
{
memset(vis, 0, sizeof(vis));
}
LL dp(int i, int j)
{
LL &ans = d[i][j];
if(vis[i][j]) return ans;
vis[i][j] = 1;
if(i > j) ans = 0;
else if(i == j) ans = 1;
else if(str[i] == str[j])
{
ans = dp(i+1, j) + dp(i, j-1) + 1;
}
else
{
ans = dp(i+1, j) + dp(i, j-1) - dp(i+1, j-1);
}
return ans;
}
void read_case()
{
init();
scanf("%s", str+1);
}
void solve()
{
read_case();
int n = strlen(str+1);
LL ans = dp(1, n);
printf("%lld\n", ans);
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
solve();
}
return 0;
}