题意:给出一个长为N的字符串,问最少插入多少字符使的原串成为一个回文串。
分析:动态规划经典问题。从右向左从小到大枚举所有子串计算各dp[i][j]的值。
状态转移方程:str[i]==str[j]:dp[i][j]=dp[i+1][j-1];
str[i]!=str[j]:dp[i][j]=min(dp[i+1][j],dp[i][j-1]);
dp[i][j]代表字符串中从i到j位置最少要插入多少字符使得原串成为回文
这题还求原字符串与字符串反过来的两个字符串的最长公共子序列的长度L,则N-L的值即为最少要插入的字符数。
Code1:
#include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <vector> #include <queue> #include <cmath> #include <map> #include <set> #define eps 1e-8 #define LL long long #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int inf=0x3f3f3f3f; const int maxn=5005; short dp[2][maxn];//滚动数组,如果直接用int dp[5000][5000]会爆内存 char s[maxn]; int n; int main() { scanf("%d",&n); scanf("%s",s+1); for(int i=1;i<=n;i++){ dp[0][i]=dp[1][i]=0; } int cur=0; for(int i=n-1;i>=1;i--){ for(int j=i+1;j<=n;j++){ if(s[i]==s[j]) dp[cur][j]=dp[!cur][j-1]; else dp[cur][j]=Min(dp[!cur][j],dp[cur][j-1])+1; } cur=1-cur; } printf("%d\n",Max(dp[0][n],dp[1][n])); return 0; }
Code2:(最长公共子序列解法)
#include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <vector> #include <queue> #include <cmath> #include <map> #include <set> #define eps 1e-8 #define LL long long #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int inf=0x3f3f3f3f; const int maxn=5005; short dp[maxn][maxn]; char s[maxn],tmp[maxn]; int n; int main() { scanf("%d",&n); scanf("%s",s+1); for(int i=0;i<=n;i++) dp[i][0]=dp[0][i]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(s[i]==s[n-j+1]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=Max(dp[i-1][j],dp[i][j-1]); } } printf("%d\n",n-dp[n][n]); return 0; }