Javascript:检查是否可以通过组合数组中的其他字符串来重新创建字符串?

时间:2011-03-21 01:59:21

标签: javascript

我正在尝试找出检查是否可以通过组合数组中的其他字符串来创建特定字符串的最佳方法。其他字符串可以是任意长度,包括一个字符。此外,其他字符串中的字符可以重新排序。

因此,如果我们正在寻找“闪避”这个词并且我们的字符串数组是['god','house','d','e','cat','c','r','jump'],那么我们就会有匹配,因为我们可以将'god','d'和'e中的字母组合在一起'创造'躲闪'。

如果数组包含“dot”而不是“d”,我们就不会有匹配,因为我们必须使用我们重新组合的每个单词中的所有字符(我们必须使用'o'和' t'以及。)

我还想知道哪些单词用于创建指定的单词,所以如果匹配,我希望函数返回重组的单词的数组索引以创建指定的单词。对于上面的“闪避”示例,它将返回[0,2,3]。

6 个答案:

答案 0 :(得分:3)

我写了一个最差情况O(2^n)的解决方案,但在大多数情况下它会表现得很好。我开始使用一个函数,将每个字符串映射到一个对象,该对象计算字符串中所有不同的字母。例如:

map("dodge") --> { "d": 2, "e": 1, "g": 1, "o": 1, size: 5 }

如您所见,它还会在结果中存储大小。这是实施:

function map(str) {
    var obj = { size: str.length };

    for(var i=0; i<str.length; i++) {   
        var ch = str.charAt(i);
        obj[ch] = (ch in obj) ? obj[ch]+1 : 1;
    }

    return obj;
}

然后我写了一个“减去”两个映射对象的函数。例如:

subtract(map("ab"), map("a")) --> { "b": 1, size: 1 }
subtract(map("dodge"), map("god")) --> { "d": 1, "e": 1, size: 1 }
subtract(map("abc"), map("abc")) --> { size: 0 }
subtract(map("a"), map("b")) --> null

正如您在上一个示例中所看到的,如果无法进行减法,则函数将返回null。这是subtract的实现:

function subtract(a, b) {
    var result = { size: 0 };

    for(var i=97; i<123; i++) { // from a to z (ASCII)
        var ch = String.fromCharCode(i);
        var diff = (a[ch] || 0) - (b[ch] || 0);

        if(diff < 0)
            return null;

        if(diff > 0) {
            result[ch] = diff;
            result.size += diff;
        }
    }
    return result;
}

最后一步是编写一个方法findCombination(word, dict),如果找到任何组合,则返回一个组合,否则返回null。例子:

var dict = ['god','house','d','e','cat','c','r','jump'];
findCombination("dodge", dict) --> [0, 2, 3]
findCombination("housecat", dict) --> [1, 4]
findCombination("hose", dict) --> null
findCombination("xyz", dict) --> null

我使用带有回溯的递归方法,我尝试从给定键中“减去”单词,直到结果为“空”:

var findCombination = function(word, dict)
{
    var solution = [];
    var mappedDict = [];

    for(var i=0; i<dict.length; i++)
        mappedDict[i] = map(dict[i]);           

    var recursiveFind = function(key,  i)
    {
        if(i == mappedDict.length)
            return false;

        var result = subtract(key, mappedDict[i])

        if(result == null)
            return recursiveFind(key, i+1);

        solution.push(i);

        if(result.size == 0 || recursiveFind(result, i+1))
            return true;

        solution.pop();
        return recursiveFind(key, i+1);
    };

    if(recursiveFind(map(word), 0))
        return solution;

    return null;
};

您可以(而且应该)通过仅初始化mappedDict变量一次来优化代码,而不是每次调用findCombination()

答案 1 :(得分:1)

算法:

  1. 将目标字符串分解为字母并对其进行分组,获取每个字母的计数
  2. 形成数组中字符串的所有排列,从1个字符串开始,直到整个数组。如果您的字符串数组很短(即<32),您可以使用带有位掩码的自动增量整数来生成所有排列。如果您的字符串数组很长(> 32),那么使用数组来存储每个“bit”插槽的字符串长度,并模拟递增整数。
  3. 跳过与目标字符串长度不同的所有排列(这应该消除所有排列的90%)。通过将“1”位数x和字符串长度(或求和时隙)相加来得到总字母数;它必须=目标字符串长度。
  4. 对于每个排列,分解字母并将它们分组,得到每个字母的计数
  5. 逐个字母地运行目标字符串组,将count与字符串组进行比较。字母和计数必须完全相同才能成功。如果是这样,请将该排列作为答案。
  6. 否则继续另一种排列
  7. 在完成所有排列后,返回失败。
  8. JavaScript实现:

    // Assume: target=target string, words_array=array of strings
    
    function groupByLetters(map, text) {
        for (var x=0; x < text.length; x++) {
            var ch = text.charAt(x);
            var n = map[ch] || 0;
            map[ch] = n + 1;
        }
    }
    
    // Split the target string into letters
    
    var target_map = {};
    groupByLetters(target_map, target);
    
    // Create permutation slots
    
    var slots = [];
    
    for (var x=0; x < words_array.length; x++) {
        // Now in order to optimize speed, store the length of each string in the slot
        // Negative = not selected, positive = selected
        slots.push(-words_array[x].length);
    }
    
    // Loop through all permutations
    
    while(true) {
        var carry = true;
        var plength = 0;
    
        for (var x=0; x < slots.length; x++) {
            var slen = slots[x];
            if (carry) {
                if (slen < 0) {     // Bit 0
                    carry = false;
                    slots[x] = -slen;   // 0->1, no carry
                } else {
                    slots[x] = -slen;   // 1->0, continue to carry
                }
            }
    
            if (slots[x] > 0) plength += slots[x];
        }
    
        if (carry) {    // We have exhausted the permutations
            return null;
        }
    
        // Now plength = total number of letters in selected permutation
    
        if (plength !== target.length) continue;    // Not the same number of letters, skip
    
        // Build map of all letters in selected permutation
    
        var pmap = {};
        var permutation = [];
    
        for (var x=0; x < slots.length; x++) {
            if (slots[x] > 0) {
                groupByLetters(pmap, words_array[x]);
                permutation.push(words_array[x]);
            }
        }
    
        // Check if the map is the same as the target map
    
        var match = true;
    
        for (var letter in target_map) {
            if (!target_map.hasOwnProperty(letter)) continue;
            if (target_map[letter] !== pmap[letter]) {
                match = false;
                break;
            }
        }
    
        if (match) return permutation;  // Success!
    }
    

    警告:我有尝试运行此功能。如果我在这里或那里打错了,请告诉我。

答案 2 :(得分:1)

修改
这应该比天真的解决方案快得多,因为所有的工作都是通过内置的indexOf搜索来完成的,这种搜索非常快,而且它可以在第一次不匹配时退出。

function match(array, word){
    var char_array = word.split(''),
        char, index;
    array = array.join('').split('');
    while(char_array.length > 0){
        char = char_array.shift();
        if ((index = array.indexOf(char)) > -1)
            array.splice(index, 1);
        else
            return false;
    }
    return true;
}

这是天真的解决方案:

function match(array, word){
    var char_array = word.split(''),
        array_elem;
    //loop over array
    for(var i=0, l=array.length; i < l; i++){
        array_elem = array[i].split('');
            //loop over the remaining chars in the word and
            //cross-check with the current array element
        for (var j=0,len =  char_array.length,index; j < len; j++)
            if ((index = array_elem.indexOf(char_array[j])) > -1){
                //char matched, remove it from both arrays
                char_array.splice(j, 1);
                array_elem.splice(index, 1);
            }
        }
    if(char_array.length < 1) return true
        else return false
}

如果这是一个问题,应该进行优化以使其更快。

答案 3 :(得分:0)

See example here →

var wordInDict = function ( word, dict ) {
    var dict = dict.slice(0), // copy dict to not mutate original
        wl = word.length,
        dl = dict.length,
        i, j, diw,
        wCount = 0;

    for (i = 0; i < wl; i++) {
        for (j = 0; j < dl; j++) {
            diw = dict[j].indexOf(word[i]); // is letter of word in dict word
            if (diw > -1) {
                wCount++;

                // remove used character from dictionary
                if (diw == dict[j].length-1) {
                    dict[j] = dict[j].slice(0, diw);
                } else if (diw == 0) {
                    dict[j] = dict[j].slice(1);
                } else {
                    dict[j] = dict[j].slice(0,diw) + 
                              dict[j].slice(diw+1,dict[j].length-1);
                }

                // letter found, so move to next letter in target word
                break;
            }
        }
    }

    return wCount == wl;
};

var tW = 'dodge';
var tD = ['god','house','d','e','cat','c','r','jump'];
var tE = ['got','house','d','e','cat','c','r','jump'];
console.log(wordInDict(tW, tD)); // true
console.log(wordInDict(tW, tE)); // false

答案 4 :(得分:0)

我刚刚意识到这个问题等同于the subset product problem,因此NP-Complete。

s(x)成为一个size方法,它通过将字符映射到素数然后返回这些素数的乘积来为每个字符串返回一个整数:

a --> 2, b --> 3, c --> 5, d --> 7, e --> 11, etc.

然后我们得到

s("abc") = 2 * 3 * 5 = 30 = 5 * 2 * 3 = s("cab")

鉴于字典A,我们现在正在寻找一个子集A'⊂ A,以便产品

p = ∏ { s(a) : a ∈ A' }
对于给定的s(key)

等于key

答案 5 :(得分:0)

我可能想到的两种解决方案

  1. 使用 Array.sort() 对给定的字符串进行排序,如果排序的数组匹配,则繁荣字符串是字谜。

  2. 写了一段原生代码

    函数置换(输入1,输入2){ if(typeof input1 === 'string' && typeof input2 === 'string' ) { const array_1 = input1.split('') const array_2 = input2.split('') 让 count2 = 0; 让计数 1 = 0 让 is_permutable = false if(array_1.length === array_2.length) { for(let j =0;j

         if(count1 === count2) {
             is_permutable = true;
         } else {
           is_permutable = false;
         }
       }
       if(is_permutable) {
         return true;
       } else {
         return false;
       }
     } else {
       return false
     }
    
     }else {
       return false;
     }
    

    }

    函数检查重复(字,数组_1){ 让计数 = 0; 让我=0; // array_1[j] = t 和 t e s t while(i

P.S 学习编码,因此感谢任何有关内存/变量管理的建议。