一道经典的区间DP处理回文串
设状态
f
[
i
]
[
j
]
f[i][j]
f[i][j] 为
s
[
i
∼
j
]
s[i\sim j]
s[i∼j] 中回文子串的个数,这里回文子串数等价于删除字符的方案数
显然,
f
[
i
]
[
j
]
f[i][j]
f[i][j] 要从
f
[
i
+
1
]
[
j
]
f[i+1][j]
f[i+1][j] 和
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1] 转移过来。
但是,在
f
[
i
+
1
]
[
j
]
+
f
[
i
]
[
j
−
1
]
f[i+1][j]+f[i][j-1]
f[i+1][j]+f[i][j−1] 中,
f
[
i
+
1
]
[
j
−
1
]
f[i+1][j-1]
f[i+1][j−1] 被算了两次,所以要减掉:
f
[
i
]
[
j
]
=
f
[
i
+
1
]
[
j
]
+
f
[
i
]
[
j
−
1
]
f[i][j]=f[i+1][j]+f[i][j-1]
f[i][j]=f[i+1][j]+f[i][j−1]
额外地,当 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j] 时,这两个字符可以跟 s [ i + 1 ∼ j − 1 ] s[i+1\sim j-1] s[i+1∼j−1] 中的所有回文串组成 f [ i + 1 ] [ j − 1 ] f[i+1][j-1] f[i+1][j−1] 个新的回文串。那么,转移方程就变成了 f [ i + 1 ] [ j ] + f [ i ] [ j − 1 ] + 1 f[i+1][j]+f[i][j-1]+1 f[i+1][j]+f[i][j−1]+1。这里的 + 1 +1 +1,是加上了由 s [ i ] , s [ j ] s[i],s[j] s[i],s[j] 组成的回文串
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const long long Maxn=66;
char s[Maxn];
long long f[Maxn][Maxn];
long long T,n;
int main()
{
// freopen("in.txt","r",stdin);
scanf("%lld",&T);
while(T--)
{
scanf("%s",s+1);
n=strlen(s+1);
for(long long i=1;i<=n;++i)
f[i][i]=1;
for(long long len=2;len<=n;++len)
{
for(long long i=1;i+len-1<=n;++i)
{
long long j=i+len-1;
if(s[i]==s[j])
f[i][j]=f[i+1][j]+f[i][j-1]+1;
else f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1];
}
}
printf("%lld\n",f[1][n]);
memset(f,0,sizeof(f));
}
return 0;
}