其一,编辑距离
衡量标准----编辑距离
字符串编辑距离:将一个字符串转化成另一个字符串,需要的最少编辑操作次数。
编辑操作为:增,删,改。
作用:用于衡量两个字符串之间的相似程度。
动态规划的公式推导
对于两个字符串 S[1...n] P[1...m] 记dis[i,j]为字符串 S[1...i]和P[1...j]的编辑距离,则编辑距离可以进行如下推导。
①S[i]==P[j] 则 dis[i,j]=dis[i-1,j-1]
②S[i]≠P[j] 则 可以进行三种操作:
S增加一个字符,使得S[i+1]==P[j] 此时 dis[i,j]=dis[i,j-1]+1
S改变一个字符,使得S[i]==P[j] 此时 dis[i,j]=dis[i-1,j-1]+1
S删除一个字符 ,此时dis[i,j]=dis[i-1,j]+1
综合以上两个情况,得到最短编辑距离为:
练习题目----模板题
//自己写的第一版 时间复杂度为On^2
#include<iostream>
using namespace std;
int dis[1005][1005];
int main(){
int n,m;
char s1[1005],s2[1005];
cin>>n;
for(int i=1;i<=n;i++) {
cin>>s1[i];
}
cin>>m;
for(int i=1;i<=m;i++) cin>>s2[i];
for(int i=0;i<=m;i++) dis[0][i]=i;
for(int i=1;i<=n;i++) dis[i][0]=i;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s1[i]==s2[j]) dis[i][j]=dis[i-1][j-1];
else{
int tmp=min(dis[i-1][j],dis[i][j-1]);
tmp=min(tmp,dis[i-1][j-1]);
dis[i][j]=tmp+1;
}
}
}
cout<<dis[n][m];
return 0;
}
//这个题自己犯的比较大的不会处理的点在于,边界。
//利用scanf("%s%d",s+1,&n);就能实现输入的字符串从s[1]开始,而不用重新开数组去替代。
//!!!高级程序设计语言没学好
#include<iostream>
#include<cstring>
using namespace std;
char s[1004][12];
int dis[12][12];
int dp(char c[12],char d[12]){
int la=strlen(c),lb=strlen(d);
char a[12],b[12];
for(int i=0;i<la;i++) a[i+1]=c[i];
for(int i=0;i<lb;i++) b[i+1]=d[i];
int dis[11][11];
for(int i=0;i<=la;i++) dis[i][0]=i;
for(int i=0;i<=lb;i++) dis[0][i]=i;
for(int i=1;i<=la;i++){
for(int j=1;j<=lb;j++){
if(a[i]==b[j]) dis[i][j]=dis[i-1][j-1];
else{
int tmp=min(dis[i-1][j],dis[i-1][j-1]);
tmp=min(tmp,dis[i][j-1]);
dis[i][j]=tmp+1;
}
}
}
return dis[la][lb];
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++) cin>>s[i];
for(int i=0;i<m;i++){
int x;
char b[12];
cin>>b>>x;
int ret=0;
for(int j=0;j<n;j++){
memset(dis,0,sizeof(dis));
if(dp(s[j],b)<=x) ret++;
}
cout<<ret<<endl;
}
return 0;
}
其二,最长公共子序列
子序列的定义
子序列:由字符串中若干字符按原相对次序构成。
动态规划的公式推导
对于两个字符串 S[1...n] P[1...m] 记 LCS[i,j]为字符串 S[1...i]和P[1...j]的最长公共子串长度,则最长公共子串长度可以进行如下推导。
①S[i]==P[j] 则 LCS[i,j]=LCS[i-1,j-1]+1;
②S[i]≠P[j] 则 LCS[i,j]=max(LCS[i-1][j],LCS[i][j-1])
练习题目---模板题
#include<iostream>
using namespace std;
const int N=1005;
int LCS[1005][1005];
int main(){
int n,m;
cin>>n>>m;
char A[1005],B[1005];
scanf("%s%s",A+1,B+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(A[i]==B[j]) LCS[i][j]=LCS[i-1][j-1]+1;
else{
LCS[i][j]=max(LCS[i-1][j],LCS[i][j-1]);
}
}
}
cout<<LCS[n][m];
return 0;
}
②最长递增子序列----只提供思路
要求最长递增子序列,可以将原序列进行一个sort,再求原序列和递增序列的最长公共子序列,这样得到的就是最长递增子序列。
但是这个子序列是递增的,而不是严格递增的。
如果要求严格递增的可以考虑;
1.排序的时候去重
2.res[i]=max(res[i],res[j]+1) j的范围是所有小于i的值