首页 > 试题广场 >

字符串最小变换次数

[编程题]字符串最小变换次数
  • 热度指数:3130 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给定两个字符串,已知可以使用三种方式进行变换
1. 插入一个字符
2. 删除一个字符
3. 更改一个字符
请设计一个算法,找到两个字符串之间的经历几次最小变换,可以字符串1转换成字符串2

数据范围:输入字符串的长度满足

输入描述:
输入两个字符串


输出描述:
最小变换次数
示例1

输入

hello
helle

输出

1
贴一篇看到的好文,这篇文章对这道题讲解的很透彻
发表于 2020-03-12 22:32:26 回复(1)
#include <iostream>
using namespace std;
int main(){

    string str1, str2;
    cin >> str1 >> str2;
    int row = str1.size() + 1, col = str2.size() + 1;
    int dp[row][col];
    for(int i = 0; i < row; ++i){
        dp[i][0] = i;
    }
    for(int i = 0; i < col; ++i){
        dp[0][i] = i;
    }
    for(int i = 1; i < row; ++i){
        for(int j = 1; j < col; ++j){
            if(str1[i - 1] == str2[j - 1]){
                dp[i][j] = dp[i - 1][j - 1];
            }else{
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            dp[i][j] = min(dp[i][j], dp[i - 1][j]  + 1);
            dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1);
        }
    }
    cout << dp[row - 1][col - 1] << endl;
    return 0;
}
发表于 2019-07-23 19:06:00 回复(0)

dp[i][j] 表示字符串s1[:i]s2[:j]达到题目要求的最小变换次数。如果字符s1[i]s2[j]相等的话,那么dp[i][j]的值就是dp[i-1][j-1],这一步不用变换,如果不相等的话,变换一个字符加一次操作,就是dp[i-1][j-1]+1。当然dp[i][j]还有可能是s1[:i-1]s2[:j]或者s1[:i]s2[:j-1]变换过来可能总的变换次数更少。所以就选其中最小的即可。实际编程应注意动态规划时有初始化,需要注意矩阵的索引和字符串索引别弄错了

int main() {
    string s1, s2;
    cin >> s1 >> s2;
    const int rows = s1.size() + 1, cols = s2.size() + 1;
    vector<vector<int> > dp(rows, vector<int>(cols, 0));
    for (int i = 0; i < rows; i++) dp[i][0] = i;
    for (int j = 0; j < cols; j++) dp[0][j] = j;
    for (int i = 1; i < rows; i++) {
        for (int j = 1; j < cols; j++) {
            if (s1[i-1] == s2[j-1]) dp[i][j] = dp[i-1][j-1];
            else dp[i][j] = dp[i-1][j-1] + 1;
            dp[i][j] = min(dp[i][j], dp[i-1][j] + 1);
            dp[i][j] = min(dp[i][j], dp[i][j-1] + 1);
        }
    }
    cout << dp[rows-1][cols-1] << endl;
    return 0;
}
发表于 2019-12-10 15:41:17 回复(0)
importjava.util.Scanner;
/*思路:
开一个二维数组d[i][j]来记录a0-ai与b0-bj之间的编辑距离,要递推时,需要考虑对其中一个字符串的删除操作、
插入操作和替换操作分别花费的开销,从中找出一个最小的开销即为所求。
具体算法:
首先给定第一行和第一列,然后,每个值d[i,j]这样计算:d[i][j] = min(d[i-1][j]+1,d[i][j-1]+1,d[i-1][j-1]+(s1[i] == s2[j]?0:1));
最后一行,最后一列的那个值就是最小编辑距离
*/
publicclassMain {
    publicstaticvoidmain(String[] args) {
        Scanner sc=newScanner(System.in);
        String str1=sc.nextLine().toLowerCase();
        String str2=sc.nextLine().toLowerCase();
        intres=minChangeSteps(str1,str2);
        System.out.println(res);
    }
    publicstaticintminChangeSteps(String s1,String s2){
        int[][] dp=newint[s1.length()][s2.length()];
        for(inti =0; i <s1.length() ; i++) {
            dp[i][0]=i;
        }
        for(inti =0; i <s2.length() ; i++) {
            dp[0][i]=i;
        }
        for(inti =1; i <s1.length() ; i++) {
            for(intj =1; j <s2.length() ; j++) {
                dp[i][j]=Math.min(dp[i-1][j]+1,
                        Math.min(dp[i][j-1]+1,dp[i-1][j-1]+(s1.charAt(i) == s2.charAt(j)?0:1)));
            }
        }
        returndp[s1.length()-1][s2.length()-1];
    }
}

发表于 2019-05-26 19:06:07 回复(1)
直接动态规划求解编辑距离
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str1;
        while((str1 = br.readLine()) != null){
            String str2 = br.readLine();
            System.out.println(solve(str1, str2));
        }
    }
    
    // 计算编辑距离
    private static int solve(String str1, String str2) {
        int m = str1.length();
        int n = str2.length();
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 1; i <= m; i++)
            dp[i][0] = dp[i - 1][0] + 1;
        for(int j = 1; j <= n; j++)
            dp[0][j] = dp[0][j - 1] + 1;
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                // 第i个字符相等,直接从前面的状态转移过来,什么都不用做
                if(str1.charAt(i - 1) == str2.charAt(j - 1))
                    dp[i][j] = dp[i - 1][j - 1];
                else      // 否则三种操作哪个消耗少用哪个
                    dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1]));
            }
        }
        return dp[m][n];
    }
}


发表于 2020-10-27 10:49:18 回复(0)
暴力递归的做法
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		
		while(sc.hasNext()) {
			
			String s1 = sc.next();
			String s2 = sc.next();
			process(s1, s2,0,0,0);
			System.out.println(min);
			
			
		}
	}
	
	//暴力递归
	private static int min = Integer.MAX_VALUE;
	public static void process(String s1, String s2, int index1, int index2, int opCount) {
		
		
		char[] carr1 = s1.toCharArray();
		char[] carr2 = s2.toCharArray();
		 if(index2 == carr2.length || index1 == carr1.length){
			 if(index1 == carr1.length  && index2 == carr2.length) {
				 if(opCount < min) {
						min = opCount;
					}
					return;
			 } 	
		 } 
		 
	 
		if(index1 < carr1.length  && index2 < carr2.length && carr1[index1] == carr2[index2]) {
			 process(s1,s2,index1+1,index2+1,opCount);//两个字符相等
		 }
		if(index1 + 1 <= carr1.length && index2 + 1 <= carr2.length)
			process(s1,s2,index1+1,index2+1,opCount+1);//替换
		
		if(index2 + 1 <= carr2.length)
			process(s1,s2,index1,index2+1,opCount+1); //插入
		if(index1 + 1 <= carr1.length)
			process(s1,s2,index1+1,index2,opCount+1); //删除
		
	}

}


发表于 2020-03-29 17:06:17 回复(0)
#include<iostream>
#include<string>
using namespace std;

const int maxn = 1005;
int dp[maxn][maxn];

int main() {
    string s1, s2;
    cin >> s1 >> s2;
    int m = s1.length(), n = s2.length();
    for(int i = 0; i <= m; i++) dp[i][0] = i;
    for(int j = 0; j <= n; j++) dp[0][j] = j;
    
    for(int i = 1; i <= m; i++) {
        for(int j = 1; j <= n; j++) {
            if(s1[i-1] == s2[j-1]) {
                dp[i][j] = dp[i-1][j-1];
            } else {
                dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
            }
        }
    }
    cout << dp[m][n] << endl;
    return 0;
}

发表于 2020-02-11 13:08:30 回复(0)
#include <bits/stdc++.h>

using namespace std;

int dp[1010][1010];
string s1, s2;

int Min(int x, int y, int z) {
	return min(x, min(y, z));
}

int DP(int l, int r) {
	if (dp[l][r] != -1) {
		return dp[l][r];
	}
	if (s1[l - 1] == s2[r - 1]) {
		dp[l][r] = DP(l - 1, r - 1);
		return dp[l][r];
	} else {
		dp[l][r] = Min(DP(l - 1, r - 1), DP(l - 1, r), DP(l, r - 1)) + 1;
		return dp[l][r];
	}
}

int main() {
	cin >> s1 >> s2;
	memset(dp, -1, sizeof(dp));
	for (int i = 0; i < (int)s1.size(); i++) {
		dp[i + 1][0] = (i + 1);
	}
	for (int i = 0; i < (int)s2.size(); i++) {
		dp[0][i + 1] = (i + 1);
	}
	dp[0][0] = 0;
	cout << DP((int)s1.size(), (int)s2.size()) << "\n";
	return 0;
}

发表于 2019-11-28 09:04:11 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    string s1, s2;
    cin>>s1>>s2;
    int n = s1.length(), m = s2.length();
    int a[n+1][m+1];
    for(int i=0;i<=n;i++)
        a[i][0] = i;
    for(int i=0;i<=m;i++)
        a[0][i] = i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(s1[i]==s2[j])
                a[i][j] = a[i-1][j-1];
            else
                a[i][j] = a[i-1][j-1] + 1;
            a[i][j] = min(a[i][j], a[i-1][j]+1);
            a[i][j] = min(a[i][j], a[i][j-1]+1);
        }
    cout<<a[n-1][m-1]<<endl;

    return 0;
}

发表于 2019-08-18 06:20:11 回复(0)
经典的编辑距离  百度编辑距离即可
dp[x][y]表示截取字符串s1从下标0 - x的字串和截距字符串s2下标0-y的字串的编辑距离
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 3;
int dp[maxn][maxn];

int main() {
    string a, b; cin>>a>>b;
	int row = a.length(), col = b.length();
	for(int i = 0; i <= row; i++) dp[i][0] = i; //初始化  空串和a的编辑距离
	for(int i = 0; i <= col; i++) dp[0][i] = i; //初始化  空串和b的编辑距离
    for(int i = 1; i <= row; i++)
        for(int j = 1; j <= col; j++) {
            if(a[i-1] == b[j-1]) dp[i][j] = dp[i-1][j-1]; //相等时的dp策略
            else {//不相等时的dp策略
                dp[i][j] = min(dp[i-1][j-1], min(dp[i][j-1], dp[i-1][j]))+1;
            }
        }
    cout<<dp[row][col]<<endl;
}
/*
hola
hello
*/


发表于 2019-12-26 15:12:40 回复(0)
import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc =  new Scanner(System.in);
        String str1 = sc.nextLine();
        String str2 = sc.nextLine();
        int[][] dis = new int[str1.length()+1][str2.length()+1];
        for(int i=0; i<=str1.length();i++){
            dis[i][0]=i;
        }
        for(int i=0; i<=str2.length();i++){
            dis[0][i]=i;
        }
        for(int i=1; i<=str1.length();i++){
            for(int j=1;j<=str2.length(); j++){
                dis[i][j]=Math.min(Math.min(dis[i-1][j]+1,dis[i][j-1]+1),dis[i-1][j-1]+(str1.charAt(i-1)==str2.charAt(j-1)?0:1));
            }
        }
        System.out.println(dis[str1.length()][str2.length()]);
    }
}

发表于 2020-08-06 20:14:11 回复(0)
import java.util.*;
public class Main{
    public static void main(String[] args){
        // 编辑距离
        Scanner scan  = new Scanner(System.in);
        String s1 = scan.nextLine();
        String s2 = scan.nextLine();
        int m = s1.length();
        int n = s2.length();
        int[][] dp = new int[m+1][n+1];
        for(int i=0;i<=m;++i)
            dp[i][0] = i;
        for(int j=0;j<=n;++j)
            dp[0][j] = j;
        for(int i=1;i<=m;++i)
            for(int j=1;j<=n;++j){
                if(s1.charAt(i-1)==s2.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1];
                
                else {
                    
                    dp[i][j]= Math.min(dp[i-1][j-1]+1, Math.min(dp[i-1][j]+1,dp[i][j-1]+1));
                }
                    
            }
        System.out.println(dp[m][n]);
    }
}

发表于 2020-07-02 20:05:41 回复(0)
编辑距离问题
1.递归解法,理解起来非常简单
import java.util.Scanner;

/**
 * @Author: coderjjp
 * @Date: 2020-05-09 21:17
 * @Description:
 * @version: 1.0
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s1 = sc.nextLine();
        String s2 = sc.nextLine();
        int ans = recursion(s1, s2, s1.length(), s2.length());
        System.out.println(ans);
    }
    private static int recursion(String s1, String s2, int i, int j) {
        //由s1 --> s2
        if (j == 0) {
            return i;
        } else if (i == 0) {
            return j;
        } else if (s1.charAt(i-1) == s2.charAt(j - 1)) {//跳过
            return recursion(s1, s2, i - 1, j - 1);
        } else {
            int m1 = recursion(s1, s2, i - 1, j) + 1;//删掉一个字符
            int m2 = recursion(s1, s2, i, j - 1) + 1;//增加一个字符
            int m3 = recursion(s1, s2, i - 1, j - 1) + 1;//替换一个字符
            return Math.min(Math.min(m1, m2), m3);
        }
    }
}
2.动态规划解法
import java.util.Scanner;

/**
 * @Author: coderjjp
 * @Date: 2020-05-09 21:17
 * @Description:
 * @version: 1.0
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s1 = sc.nextLine();
        String s2 = sc.nextLine();
        int ans = dynamicPlanning(s1, s2);
        System.out.println(ans);
    }
    private static int dynamicPlanning(String s1, String s2){
        int len1 = s1.length();
        int len2 = s2.length();
        int dp[][] = new int[len1+1][len2+1];
        for (int j = 1; j <= len2; j++)
            dp[0][j] = j;
        for (int i = 1; i <= len1; i++)
            dp[i][0] = i;
        for (int i = 1; i <= len1; i++)
            for (int j = 1; j <= len2; j++){
                if (s1.charAt(i - 1) == s2.charAt(j - 1))
                    dp[i][j] = dp[i-1][j-1];
                else
                    dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
            }
        return dp[len1][len2];
    }
}



编辑于 2020-05-10 09:14:30 回复(0)
// 假定变换规则是 1. 串1长度大于串2的时候,删除字符
// 2. 串1长度小于串2的时候,添加字符
// 3. 串1长度等于串2的时候,替换字符
// 如果仅仅是这种思路,那么只能通过50%。。。
var change=readline().split('');
var old=readline().split('');
function dp(old,mynow){
    // 四种情况
    let old_len=old.length;
    let now_len=mynow.length;
    // 跳过,看下一个数
    // 插入,删除,更改
    let ins_spl=mynow.slice(0,now_len-1);
    let del_spl=old.slice(0,old_len-1)
    let ins_old=old.slice(0,old_len)
    let del_now=mynow.slice(0,now_len)
    // 结束条件: 1. 如果数字一致,那么后面两个数不用改变
    if(now_len==1&&old_len==1&&old[0]==mynow[0]){
        return 0
    }else if(now_len==1||old_len==1){
        // 2. 否则改变一次才可以
        return 1
    }
    if(mynow[now_len-1]==old[old_len-1]){
        return dp(del_spl,ins_spl)
    }else{
        return 1+Math.min(dp(ins_old,ins_spl),dp(del_spl,del_now),dp(del_spl,ins_spl))
    } 
}
console.log(dp(old,change))

发表于 2020-04-07 16:54:32 回复(0)
qnk头像 qnk
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
{
    string a,b;
    cin>>a>>b;
    int an=a.size();
    int bn=b.size();
    int dp[1001][1001]={0};
    for(int i = 0; i <= an; ++i){
        dp[i][0] = i;
    }
    for(int i = 0; i <= bn; ++i){
        dp[0][i] = i;
    }
    for(int i=1;i<=an;i++)
    {
        for(int j=1;j<=bn;j++)
        {
            if(a[i]==b[j])
                dp[i][j]=dp[i-1][j-1];
            else
            {
                int temp=min(dp[i-1][j]+1,dp[i][j-1]+1);
                dp[i][j]=min(dp[i-1][j-1]+1,temp);
            }
        }
    }
    cout<<dp[an][bn];
    return 0;
}
经典动态规划,6ms
发表于 2020-02-17 19:50:51 回复(0)
#include<iostream>
#include<string>
#include<vector>
using namespace std;

int main(){
    string s1,s2;
    cin>>s1>>s2;
    int count = 0;
    int row = s1.length();
    int col = s2.length();
    int dp[row][col] ;//表示s10-s1i到s20-s2j的修改距离,包括添加,删除,或者更改
    for(int i = 0;i<row;i++)
        dp[i][0] = i;
    for(int j = 0;j<col;j++)
        dp[0][j] = j;
    for(int i = 1;i<row;i++){
        for(int j = 1;j<col;j++){
            dp[i][j] = min(dp[i-1][j]+1,dp[i][j-1]+1);
            dp[i][j] = min(dp[i][j],dp[i-1][j-1]+(s1[i]==s2[j]?0:1));
        }
    }
    cout<<dp[row-1][col-1]<<endl;
    
}

发表于 2019-11-22 18:23:17 回复(0)
#include <bits/stdc++.h>
using namespace std;
#define Up(i,a,b) for(int i = a; i <= b; i++)
#define Down(i,a,b) for(int i = a; i >= b; i--)
#define ms(a,x) memset(a,x,sizeof(a))
const int maxn = 1001;

int dp[maxn][maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    ms(dp,0);    //初始化
    string s1,s2;
    cin >> s1 >> s2;
    int len1 = s1.length();
    int len2 = s2.length();
    Up(i,0,len1)
    {
        dp[i][0] = i;
    }
    Up(i,0,len2)
    {
        dp[0][i] = i;
    }
    Up(i,1,len1)
    {
        Up(j,1,len2)
        {
            dp[i][j] = s1[i-1]==s2[j-1] ? dp[i-1][j-1] : min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
        }
    }
    cout << dp[len1][len2] << endl;
    return 0;
}

发表于 2019-10-16 22:07:14 回复(0)
编辑距离,动态规划

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<stack>
#include<queue>
#include<cstdio>
#include<cstring>
#include<numeric>
#include<limits>
#include<cmath>
using namespace std;

int solve(string &s1, string &s2) {
    int m = s1.size(), n = s2.size();
    int ans = 0;
    
    int dp[m+1][n+1];
    for(int i = 0; i <= m; i++){
        dp[i][0] = i;
    }
    for(int i = 0; i <= n; i++){
        dp[0][i] = i;
    }
    int d;
    for(int i = 1; i < m + 1; i++){
        for(int j = 1; j < n + 1; j++){
            if(s1[i-1] == s2[j-1]){
                d = 0;
            }else{
                d = 1;
            }
            dp[i][j] = min(min(dp[i-1][j]+1, dp[i][j-1]+1), dp[i-1][j-1]+d);
        }
    }
    return dp[m][n];
}

int main(){
    // freopen("./t2.txt", "r", stdin);
    string s1;
    string s2;
    cin >> s1 >> s2;

    int ans = solve(s1, s2);
    cout << ans;
    return 0;
}



发表于 2019-09-16 02:01:10 回复(0)
import java.util.Scanner;
public class AtoB {
    //一个字符串变成另一个字符串,允许的每次操作是增加/修改/删除一个字符,求最少的操作次数
    //我的思路是:研究出了一种规律,在最长公共子串的基础上,长的那个的不同字符数就是答案
    //如果是长度相同的两个字符串,如果最后一个相同的字符的位置在第一个不同的之后就要+1,如:
    //hello helle ,4 < 5 不用+1
    //helol hello ,5 < 4 +1
    static int last_com=-1;
    static int first_dif=-1;
    public static int common(String a,String b) {
        //动态规划最长公共子串
        //状态转换方程,等于则在左上角+1,意思是两个字符串同时扩大符合;否则就在两个字符串单个扩大下找最大
        int alen=a.length();
        int blen=b.length();
        int[][] dp=new int[alen+1][blen+1];
        for(int i=1;i<=alen;i++) {
            for(int j=1;j<=blen;j++) {
                if(a.charAt(i-1)==b.charAt(j-1)) {
                    dp[i][j]=dp[i-1][j-1]+1;//表的下标和字符串的不一样
                    last_com=i;//记录最后一个相同的下标
                }
                else {
                    if(i==j&&first_dif==-1) first_dif=i;
                    dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }
        return dp[alen][blen];
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            String a = sc.nextLine();
            String b = sc.nextLine();
            int res = common(a,b);
            res = Math.max(a.length(), b.length())-res;//不相同的字符数,在长的那一个字符
            if(a.length()!=b.length()) System.out.println(res);
            else {
                if(last_com>first_dif)//判断是否最后不同在最开始相同之后
                    System.out.println(res+1);
                else System.out.println(res);
            }
        }
        sc.close();
    }
}
发表于 2019-09-10 09:25:11 回复(0)