解析:2016-C-C题10密码脱落_哔哩哔哩_bilibili
首先如果我们要补的话可以从左边补也可以从右边补,也就是有两种选择,我们选一个最优解就可以了:
我们发现这是一个树,一直递归下去,直到补齐了为止。
而我们也并不需要真的补,我们可以移动指针,假设补左边,右边为了和左边这个虚拟端点对齐,右边指针就要往里走。补右边同理,代码如下:
#include<bits/stdc++.h>
using namespace std;
int ans;
int dfs(char *s,int left,int right,int cnt)
{
if(left>=right)return cnt;
if(*(s+left)!=*(s+right)) //两端不相同
return min(dfs(s,left+1,right,cnt+1),dfs(s,left,right-1,cnt+1));
else return dfs(s,left+1,right-1,cnt);
}
int main()
{
char s[1000];cin>>s;
int len=strlen(s);
int l=0,r=len-1;
ans=dfs(s,l,r,0);
cout<<ans;
return 0;
}
我们发现上面的代码是实现了功能的,但是在时间复杂度上过不去。因为给的数据是10000,而我们这个代码是一个二叉树,每一叉都是
总共就是,
我们知道2的10次方是1024,,20次方是
,30次方是,而我们需要,肯定是超时了。
区间dp法
解析:AcWing 1222. 密码脱落(每日一题·春季) - AcWing 思想
若整个串是回文串,则表示无脱落,否则有脱落,脱落多少个相当于去掉多少个字符,使得整个串是回文串,即可以求最长回文子序列的长度
dp图
code
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1010
using namespace std;
char s[N];
int f[N][N];
int main()
{
scanf("%s",s);
int n=strlen(s);
//区间dp的常用写法
//先枚举长度
for(int len=1;len<=n;len++)
{
//再枚举左端点
for(int i=0;i+len-1<n;i++)
{
//再1枚举右端点
int j=i+len-1;
if(len==1)//只有一个字符,那么回文串的长度就是1
f[i][j]=1;
else
{
f[i][j]=max(f[i+1][j],f[i][j-1]);
if(s[i]==s[j]) //两个端点都存在
f[i][j]=max(f[i][j],f[i+1][j-1]+2);
}
}
}
printf("%d\n",n-f[0][n-1]); //f[0][n-1]是最长回文子序列,n-f[0][n-1]就是脱落的字符的数量
return 0;
}
时间复杂度: 状态数量是,状态转移是,所有总共时间复杂度为。所以可以过掉