CF245H
好题笔记
这是一道计数和记忆化搜索的好题,算是我直接用这道题来讲这两个知识点。
记忆化搜索: 我们这题一眼看起来有点mancher的感觉,但是其实这道题并不是mancher,而是道计数dp,我们其实可以一下子就像出来状态dp[x][y] 从x到y的回文子串的个数,那么通过容斥原理我们可以知道dp[x][y]=dp[x+1][y]+dp[x][y-1]-dp[x+1][y-1] +if(x==y) 这个还是很容易想出来的一个转移方程,至于状态的想法其实直接就是计数dp的感觉,转移方程就是在草稿纸上画一画就欧克了。
现在我们扯到记忆化搜索:我们定义一个vis[x][y] 只要之前找到过就直接返回,之后记忆化搜索还需要一点点的边界处理,因为我们之前每一次都有记录所以说我们直接先把所有的都找一遍也不会太慢。
之后就是计数dp,其实它本质上来讲并不算是dp,只是他的转移方程和dp有点像,我们一般做找转移方程直接用容斥原理就OK。
最后代码如下
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char s[5005];
void get_input() {
scanf("%s", s+1 );
}
bool if_pal[5005][5005];
bool visit_pal[5005][5005];
void get_pal(int l,int r){
if(visit_pal[l][r])
return; //记忆化搜索的标志
if(l==r||l==r-1) {
if_pal[l][r] = (s[l]==s[r]);
return;
} //边界搜索
get_pal(l+1,r-1);
if(if_pal[l+1][r-1])
if_pal[l][r]= (s[l] == s[r]);
visit_pal[l][r]=true;//记忆化搜索的另一个标记
}
int dp[5005][5005];
bool vis_dp[5005][5005];
void get_dp(int l,int r){
if(vis_dp[l][r])
return;
if(l==r){
dp[l][r]=1;
return ;
}
if(l==r-1){
if(s[l]==s[r]){
dp[l][r]=3;
}
else dp[l][r]=2;
return;
}
get_dp(l+1,r);
get_dp(l,r-1);
get_dp(l+1,r-1);
dp[l][r]=dp[l+1][r]+dp[l][r-1]-dp[l+1][r-1]+if_pal[l][r];
vis_dp[l][r]=true;
}
void init(){
int len=strlen(s+1);
for(int i=1;i<=len;i++){
for(int j=i;j<=len;j++){
get_pal(i,j);
}
}
for(int i=1;i<=len;i++){
for(int j=i;j<=len;j++){
get_dp(i,j);
}
}
}
void get_ans(){
int q,r,l;
scanf("%d",&q);
while(q--){
scanf("%d%d",&l,&r);
printf("%d\n",dp[l][r]);
}
}
int main(){
get_input();
init();
get_ans();
return 0;
}
最后我最近改了写代码的习惯,把主函数里的东西写少可以有效的方便减少bug的数量。