【算法】list1-求最小操作数

更多题目【开发】逻辑题&算法题&语言特性(集合贴)  

求最小操作数

题目详情:
给了A、B两个单词和一个单词集合Dict,每个的长度都相同。我们希望通过若干次操作把单词A变成单词B,每次操作可以改变单词中的一个字母,同时,新产生的单词必须是在给定的单词集合Dict中。求所有行得通步数最少的修改方法。
举个例子如下:
Given:
   A = "hit"
   B = "cog"
   Dict = ["hot","dot","dog","lot","log"]
Return
 [
   ["hit","hot","dot","dog","cog"],
   ["hit","hot","lot","log","cog"]
 ]
    即把字符串A = "hit"转变成字符串B = "cog",有以下两种可能:
    "hit" -> "hot" ->  "dot" ->  "dog" -> "cog";
    "hit" ->  "hot" ->  "lot" ->  "log"  ->"cog"。
答题说明:
A和B相同的情况下不需要做转换,此时直接返回空集;
main函数是为方便你在提交代码之前进行在线编译测试,可不完成。
经过两个朋友的一起讨论,给出的结论就是利用动态规划。 
时间复杂度是 O(字母数 * 转换单词长度 * 字典单词数) 
时间复杂度是 O(字母数 * 转换单词长度 * 字典单词数)

解题思路:

这让我想到大学里面的动态规划,于是仔细回忆曾经学过的东西,用手画出上面个东西。大概思路就是,对每个步骤进行分解,从第一步开始,用每个变化后的值到字典中寻找对应的项,如果存在,则将路径进行记录,并推入堆栈。然后依次类推。

师傅的思路:(这个啰嗦而纠结的男人又唠叨了一把,念念叨叨说他以前参赛最先放弃的就是这种实现比分析困难多倍的题目)

什么数量限定,不知道效率需求。简单来说,你把dictionary里面的单词以字母和位数作为边,构建一个转换的图,然后从你的起始点做广度遍历到目标点就OK了。。。最短路径都是WFS的问题,哪里有动态规划的事情了。。。

各种答案:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.felidae.gcj;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

/**
 *
 * @author rock
 */
public class CharacterTransaction {
    
    public int getMinimal(String[] dictionary, String src, String dest) {
        Set<String> dict;        
        dict = new HashSet<>();
        dict.addAll(Arrays.asList(dictionary)); 
        dict.remove(src);
        dict.remove(dest);
        
        Queue<String> queue = new LinkedList<>();
        Map<String, Integer> visited = new HashMap<>();
        Map<String, String> lastStr = new HashMap<>();
        
        queue.add(src);
        visited.put(src, 0);
        while (!queue.isEmpty()) {
            String word = queue.poll();
            int length = visited.get(word);
            char[] chars = word.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                for (int j = 'a'; j <= 'z'; j++) {
                    char oldChar = chars[i];
                    chars[i] = (char)j;
                    String newStr = new String(chars);
                    if (newStr.equals(dest)) {
                        lastStr.put(newStr, word);
                        String str = dest;
                        while (str != null) {
                            System.out.println(str);
                            str = lastStr.get(str);
                        }
                        return length + 1;
                    }
                    if (!visited.containsKey(newStr) && dict.contains(newStr)) {
                        queue.add(newStr);
                        visited.put(newStr, length + 1);
                        lastStr.put(newStr, word);
                    }
                    chars[i] = oldChar;
                }
            }
        }
        
        return -1;
    }
    
    public static void main(String[] args) {
        CharacterTransaction characterTransaction = new CharacterTransaction();
        System.out.println(characterTransaction.getMinimal(new String[]{"hit", "hgt", "wez", "wiz","zgt", "ait", "abt", "wit"}, "zit", "wea"));
    }
}

function inArray(val, ary)
{
    if (typeof ary !== 'object') return -1

    for (var i = 0; i < ary.length; i++) {
        if (val === ary[i]) return i
    }

    return -1
}

function getNextSteps(word, tailWord, dict)
{
    var steps = []

    for (var i = 0; i < word.length; i++) {
        var tmpReg = new RegExp(
                '^' + word.slice(0, i) + '.' + word.substring(i + 1) + '$',
                'i')

        for (var j = 0; j < dict.length; j++) {
            if (tmpReg.test(dict[j])) {
                steps.push(dict[j])
                if (dict[j] != tailWord) {
                    delete dict[j]
                }
            }
        }
    }

    return steps;
}

function searchPath(headWord, tailWord, dict)
{
    if (headWord === tailWord) return []

    var h = inArray(headWord, dict)
    if (h >= 0) delete dict[h]

    var t = inArray(tailWord, dict)
    if (t < 0) dict.push(tailWord)

    var result  = []
    var curPath = []

    var search = function (word) {
        curPath.push(word)
        var steps = getNextSteps(word, tailWord, dict)

        if (steps.length == 0) {
            curPath.pop()
            return
        } else {
            for (var i = 0; i < steps.length; i++) {
                if (steps[i] === tailWord) {
                    var tmp = curPath.concat(tailWord)
                    if (tmp.length < result.length || result.length == 0) {
                        result = tmp
                    }
                    continue
                }
                search(steps[i])
            }
            curPath.pop()
        }
    }

    search(headWord)

    return result 
}

function test(){
    var headWord = 'mrong'
    var tailWord = 'xkang'
    var dict = [
        'mrong',
        'xrong', 'xkong',
        'mrang', 'xrang',
        'grong', 'gkong', 'gkang',
        'mrcng', 'mrcog', 'xrcog', 'xkcog', 'xkaog',
        'xkang'
    ]
    // mrong -> xrong -> xkong -> xkang
    // mrong -> mrang -> xrang -> xkang
    // mrong -> grong -> gkong -> gkang -> xkang
    // mrong -> mrcng -> mrcog -> xrcog -> xkcog -> xkaog -> xkang

    var result = searchPath(headWord, tailWord, dict)
    console.log(result.length + ':' + result.join('->'))
}

test();

// 自己的大作。。。。唉。。。。

function WordPath(){
    var queue = [];    // [string]
    var visited = {};  // {string:int}
    var pathMap = {};  // {string:string}

    var charArr = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
    function inArray(str,dict){
	if (typeof dict == "object" && dict.hasOwnProperty("length")){
	    for (var i = 0;i < dict.length;i++){
                if (str == dict[i]){
	            return i;
	        }
	    }
	}
	return -1;
    } // return int
    
    function print(dest,pathMap){
    	var headStr = dest;
	var nextStr = pathMap[dest];
	var num = 0;
	console.info(pathMap);
	do{
	    num++;
	    console.info(headStr + "=>" + nextStr);
	    headStr = nextStr;
	    nextStr = pathMap[headStr];
	}while(nextStr);
	console.info(num);     
    } // return void

    this.printPath = function(dict,src,dest){
	queue.push(src);
	visited[src] = 0;
	while (queue.length) {
	    var curStr = queue.shift();
	    var curStrArr = curStr.split("");
	    for (var i = 0; i < curStrArr.length; i++) {
		for (var j = 0; j < charArr.length; j++) {
		    var oldChar = curStrArr[i];
	  	    curStrArr[i] = charArr[j];
		    var newStr = curStrArr.join("");
		    if (newStr == dest) {
			pathMap[newStr] = curStr;
			visited[newStr]++;
			print(dest,pathMap);
			return false;
		    }
		    if (!pathMap[newStr] && inArray(newStr,dict) != -1) {
		        pathMap[newStr] = curStr;
			queue.push(newStr);
			visited[newStr]++;
		    }
		    curStrArr[i] = oldChar;
		}
	    }
	}  
    };   
}

var path = new WordPath();
path.printPath(["hot","dot","dog","lot","log"],"hit","cog");

运行结果:

localhost:~ marong$ node /********
{ hot: 'hit',
  dot: 'hot',
  lot: 'hot',
  dog: 'dot',
  log: 'lot',
  cog: 'dog' }
cog=>dog
dog=>dot
dot=>hot
hot=>hit
4
localhost:~ marong$

运行结果:

测试数据为包含10000个长度为4的字符串,起始分别是 “aaaa”,”zzzz“。

用上面第三种算法运行之后的结果为:

zzzz=>ozzz
ozzz=>ozfz
ozfz=>fzfz
fzfz=>fzff
fzff=>fvff
fvff=>uvff
uvff=>ufff
ufff=>uffl
uffl=>ufel
ufel=>efel
efel=>enel
enel=>enea
enea=>wnea
wnea=>wyea
wyea=>wyza
wyza=>waza
waza=>waca
waca=>aaca
aaca=>aaaa
19
34256

但是师傅用他写的java跑相同数据,得出来的结果跟我相同,时间为 300+毫秒。也就是说比我写的快了平方个数量级的毫秒时间。他指出,inArray的实现方式很糟糕,让算法复杂度从 n变为n的平方。他觉得可以想办法利用json对象来实现查找。哈哈,虽然他不会写js,但是却一下子说到点子上了。我立马就反应过来了。将程序进行了修改:

function WordPath(){
    var queue = [];    // [string]
    var visited = {};  // {string:int}
    var pathMap = {};  // {string:string}
    var dictJson = {}; // {string:int}

    var charArr = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
    
    // 新增的将数组转换为json对象的方法
    function arrayToJson(arr){
    	var jsonObj = {};
    	if (typeof arr == "object" && arr.hasOwnProperty("length")){
	    	for (var i = 0;i < arr.length;i++){
                jsonObj[arr[i]] = i+1;
	    	}
		}
		return jsonObj;
    } // return json
    
    function print(dest,pathMap){
    	var headStr = dest;
		var nextStr = pathMap[dest];
		var num = 0;
		console.info(pathMap);
		do{
	    	num++;
	    	console.info(headStr + "=>" + nextStr);
	    	headStr = nextStr;
	    	nextStr = pathMap[headStr];
		}while(nextStr);
		console.info(num);     
    } // return void

    this.printPath = function(dict,src,dest){
       dictJson = arrayToJson(dict); // 通过新增的方法,获取字典数组的json对象
	queue.push(src);
	visited[src] = 0;
	while (queue.length) {
	    var curStr = queue.shift();
	    var curStrArr = curStr.split("");
	    for (var i = 0; i < curStrArr.length; i++) {
		for (var j = 0; j < charArr.length; j++) {
		    var oldChar = curStrArr[i];
	  	    curStrArr[i] = charArr[j];
		    var newStr = curStrArr.join("");
		    if (newStr == dest) {
				pathMap[newStr] = curStr;
				visited[newStr]++;
				print(dest,pathMap);
				return false;
		    }
                  // 通过判断目标字符串在dictJson中对应的值,判断其是否存在
		    if (!pathMap[newStr] && dictJson[newStr]) {
		        pathMap[newStr] = curStr;
				queue.push(newStr);
				visited[newStr]++;
		    }
		    curStrArr[i] = oldChar;
		}
	    }
	}  
    };   
}

// 时间的耗费为:228s


转载于:https://my.oschina.net/maomi/blog/147148

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 最大公因数是指两个或多个整数共有约数中最大的一个数,可以使用欧几里得算法(辗转相除法)来解。 最小公倍数是指两个或多个整数公有的倍数中最小的一个数,可以通过先出最大公因数,然后用两数之积除以最大公因数来解。 ### 回答2: Python是一种简洁而强大的编程语言,可以轻松实现许多算法和数学操作,包括最大公因数和最小公倍数。 最大公因数(GCD)指两个或多个数的最大公约数,可以使用欧几里得算法来计算。简单来说,欧几里得算法就是反复地将两个数中较大的数除以较小的数,直到余数为0为止,此时较小的数就是它们的最大公约数。 因此,我们可以使用Python来实现欧几里得算法,例如: ``` def gcd(a, b): if b == 0: return a else: return gcd(b, a % b) ``` 这个函数接受两个参数a和b,递归地将b和a%b(即a除以b的余数)作为参数传递,直到b为0为止,此时a就是最大公约数。 最小公倍数(LCM)指两个或多个数的最小公倍数,可以用它们的乘积除以它们的最大公约数来计算。因此,我们可以使用gcd函数来计算最小公倍数,例如: ``` def lcm(a, b): return a * b / gcd(a, b) ``` 这个函数接受两个参数a和b,先计算最大公约数,然后将a和b的乘积除以最大公约数得到最小公倍数。 使用这些函数需要注意的是,它们的参数应该是整数,如果使用浮点数则可能导致计算错误。另外,在计算最小公倍数时,可能会产生很大的整数,超出了Python的整数表示范围,因此需要使用其他方法来处理。 ### 回答3: 最大公因数和最小公倍数是数学中常见的概念,最大公因数和最小公倍数的方法有很多种,包括辗转相除法、欧几里得算法、质因数分解法等。在 Python 中,我们可以利用这些方法来解。 一、辗转相除法 辗转相除法,也叫欧几里得算法,是最大公因数的一种方法。这个算法的核心思想是将较大的数除以较小的数,再用余数作为被除数继续进行除法运算,直到余数为零,此时最后的被除数就是最大公因数。 以两个数的最大公因数为例,代码如下: ```python def gcd(x, y): while y: x, y = y, x % y return x ``` 其中,`x` 和 `y` 是需要得最大公因数的两个数。 二、质因数分解法 质因数分解法是最小公倍数和最大公因数的一种方法。其核心思想是将每个数分解成质数乘积的形式,再将这些质数乘积中重复的部分取最大值作为最大公因数,取不重复的部分乘起来作为最小公倍数。 以两个数的最大公因数和最小公倍数为例,代码如下: ```python def prime_factors(n): i = 2 factors = [] while i * i <= n: if n % i: i += 1 else: n //= i factors.append(i) if n > 1: factors.append(n) return factors def gcd(x, y): x_factors = prime_factors(x) y_factors = prime_factors(y) intersection = list(set(x_factors) & set(y_factors)) product = 1 for factor in intersection: product *= factor return product def lcm(x, y): x_factors = prime_factors(x) y_factors = prime_factors(y) union = set(x_factors) | set(y_factors) product = 1 for factor in union: product *= factor return product ``` 其中,`prime_factors` 函数是质因数分解的函数,`gcd` 和 `lcm` 函数分别是最大公因数和最小公倍数的函数,`x` 和 `y` 是需要得最大公因数和最小公倍数的两个数。 三、小结 本文详细介绍了 Python 中最大公因数和最小公倍数的几种方法,包括辗转相除法和质因数分解法。不同的方法适用于不同的场合,需要根据具体问题进行选择。同时,Python 中有很多内置函数可以方便地完成这些计算,比如 `math.gcd` 函数可以直接最大公因数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值