题目链接
题目描述
题目大意
给你n个串(最长不超过60),问有多少种删去字符的方法使剩下的字符成为一个回文子串。
题目思路
仔细思考会发现其实这个题目可以等价于这个字符串有多少个回文子序列。
思路:dp[ i ][ j ]表示从第 i 个字符到第 j 个字符之间的回文串的数目。
当str[ i ]==str[ j ]的时候,要考虑i+1, j 组成的回文串,i ,j -1之间的回文串,但是两者都会计算i+1,j-1的回文串数,所以要减去,另外,因为str[ i ]==str[ j ],所以i+1,j-1之间的回文串也能和str[ i ],str[ j ]组成回文串,所以还要加上i+1,j-1之间的回文串。另外,str[ i ]与str[ j ]自身也
组成了回文串。
str[ i ] != str [ j ] 时 dp[ i ][ j ] = dp[ i+1 ][ j ] + dp[ i ][ j -1] - dp[ i+1 ][ j-1 ]
str[ i ] == str [ j ] 时 dp[ i ][ j ] = dp[ i+1 ][ j ] + dp[ i ][ j -1] +1 ;
实话实说很容易想到是这么分类讨论的,但是转移方程式真的有的难想,特别是当str[i]==str[j]时
记忆化搜索
pass:觉得记忆化搜索真的挺无脑比较实用
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=60+5;
typedef long long ll;
int t,d;
char s[maxn];
ll dp[maxn][maxn];//注意要开ll
ll dfs(int l,int r){
if(dp[l][r]!=-1){
return dp[l][r];
}else if(l==r){
return dp[l][r]=1;
}else if(l>r){
return dp[l][r]==0;
}else if(s[l]==s[r]){
return dp[l][r]=dfs(l+1,r)+dfs(l,r-1)+1;
}else if(s[l]!=s[r]){
return dp[l][r]=dfs(l+1,r)+dfs(l,r-1)-dfs(l+1,r-1);
}
}
int main(){
scanf("%d",&t);
while(t--){
memset(dp,-1,sizeof(dp));
scanf("%s",s+1);
d=strlen(s+1);
printf("%lld\n",dfs(1,d));
}
return 0;
}
区间dp
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=60+5;
typedef long long ll;
int t,d;
char s[maxn];
ll dp[maxn][maxn];//注意要开ll
int main(){
scanf("%d",&t);
while(t--){
memset(dp,0,sizeof(dp));
scanf("%s",s+1);
d=strlen(s+1);
for(int i=1;i<=d;i++){
dp[i][i]=1;
}
for(int i=d;i>=1;i--){
for(int j=i+1;j<=d;j++){
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[1][d]);
}
return 0;
}