当前位置:  开发笔记 > 编程语言 > 正文

计算常用字符的频率

如何解决《计算常用字符的频率》经验,为你挑选了1个好方法。

我怎样才能编写一个比较两个字符串的函数,并返回一个包含两个字符串中出现的字符的对象,以及它们出现的次数?

这是一些示例输入和输出:

"Leopard", "Lion"    // { "L": 2, "o": 2 }
"Hello",   "World"   // { "l": 3, "o": 2 }
"Flower",  "Power"   // { "o": 2, "w": 2, "e": 2, "r": 2 }
"phone",   "Memphis" // { "p": 2, "h": 2, "e": 2 }

小智.. 5

如果您正在处理小字符串,则无需复杂化.以下方法很简单,在处理非常大的字符串时会牺牲性能.

首先,我们需要定义一个对象来保存重复字符的计数.

然后我们需要迭代每个字符串.迭代字符串有很多方法,但在下面的例子中我要么:

Array#forEach使用Function#call方法(ES5示例)调用字符串,或

for...of循环(ES6示例).

这两种方法都有效,因为字符串是内置可迭代对象的一个​​示例.

在迭代字符串时,我们需要首先检查重复对象中是否存在该字符.

如果是,我们不需要再次检查,只需增加数字.

如果没有,则扫描另一个字符串以查看该字符是否出现在那里.

如果是,则递增该字符的重复计数器.

如果没有,什么都不做.

使用此方法,您不会计算不重复的字符,也不会尝试验证已经验证的字符.这可以通过更长的字符串提供一些显着的速度提升

完成两个字符串的迭代后,只需返回重复的holder对象即可.

ES6示例

const countLetters = (v1, v2) => {
  const out = {};
  
  for(let char of v1) (out[char] || v2.indexOf(char) !== -1) && (out[char] = (out[char] || 0) + 1);
  for(let char of v2) (out[char] || v1.indexOf(char) !== -1) && (out[char] = (out[char] || 0) + 1);

  return out;
}

const test = (v1,v2) => (console.log(JSON.stringify(countLetters(v1, v2))),test);

test("Leopard", "Lion")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }

ES5示例

var countLetters = function(v1, v2) {
  var out = {};
  
  Array.prototype.forEach.call(v1, function(char) {
    if(out[char] || v2.indexOf(char) !== -1) out[char] = (out[char] || 0) + 1;
  });
  Array.prototype.forEach.call(v2, function(char) {
    if(out[char] || v1.indexOf(char) !== -1) out[char] = (out[char] || 0) + 1;
  });

  return out;
}

var test = function(v1,v2){ return (console.log(JSON.stringify(countLetters(v1, v2))),test) };

test("Leopard", "Lion")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }


当您开始使用非常大的字符串时,性能会成为一个问题.以下方法使用一些算法魔法来获得性能.

首先,找到两个字符串中每个字母的频率.使用排序数组比使用未排序数组更快,并节省了一些时间,因为我们可以将字符串拆分为常见字符组而不是计算每个字符.

我使用一个Map对象来利用这个Map#has方法 - 这比Array#indexOf下一个函数要快得多.

// Calculate the frequency of each character in a string
const calculateCharacterFrequency = string => {
   // Split the string into individual characters
    const array = [...string];
    // Sort the split string
    const sorted = array.sort();
    // Join the split string
    const joined = sorted.join("");
    // Split the string into groups of common characters
    const matches = joined.match(/(.)\1*/g);
    // Iterate the groups
    const frequency = matches.reduce(
        (frequency, character) => { 
            // Insert char and frequency into map
            frequency.set(character[0], character.length);
            // return frequency map for use in next iteration
            return frequency;
        },
        // This is the map that will be passed as `frequency`
        new Map()
    );
    // Return the map
    return frequency;
};

接下来,找到每个字符串之间的公共字符,并创建一个包含每个公共字符频率的映射.

这里最大的性能提升是创建一个独特的字符集中出现的所有字符集.我利用Set对象的独特性来从数组中删除重复项,还有其他方法可以从数组中删除重复项,但这是我测试过的最快的重复项.这样,我们只迭代一个unqiue集并检查两个映射中是否出现该字符.

// Calculate the frequency of each character two strings have in common
const calculateCommonCharacterFrequency = (v1, v2) => {
    // Get frequency map of first string
    v1 = calculateCharacterFrequency(v1);
    // Get frequency map of second string
    v2 = calculateCharacterFrequency(v2);
    // Create an array containing a list of all characters occuring in either string
    const combinedCharacterArray = [...v1.keys(), ...v2.keys()];
    // Remove duplicate characters
    const combinedUniqueCharacterSet = new Set(combinedCharacters);
    // Convert set back to array
    const combinedUniqueCharacterArray = Array.from(combinedUniqueSet);
    // Iterate the unique array of common characters
    const commonFrequency = combinedUniqueCharacterArray.reduce(
        (commonFrequency, character) => {
            // Check if both strings have the character
            const isCommon = v1.has(character) && v2.has(character);
            if(isCommon) {
                // Find the sum of the frequency of the character in both strings
                const combinedFrequency = v1.get(character) + v2.get(character);
                // Insert character and frequency into map
                commonFrequency.set(character, combinedFrequency);
            }
            // return frequency map for use in next iteration
            return commonFrequency;
        }, 
        // This is the map that will be passed as `commonFrequency`
        new Map()
    );
    // Return the map
    return commonFrequency;
};

浓缩的例子

上面的示例已经扩展,以便于阅读和解释.以下示例已经过浓缩以节省空间.

const calcCharFreq = string => [...string].sort().join("").match(/(.)\1*/g).reduce((freq, char) => (freq.set(char[0], char.length), freq), new Map());

const calcCommonCharFreq = (v1, v2) => {
    v1 = calcCharFreq(v1);
    v2 = calcCharFreq(v2);
    return Array.from(new Set([...v1.keys(), ...v2.keys()])).reduce((dup, char) => ((v1.has(char) && v2.has(char)) && dup.set(char, v1.get(char) + v2.get(char)), dup), new Map());
};

const test = (v1,v2) => (console.log('{ '+Array.from(calcCommonCharFreq(v1, v2)).reduce((pairs, value) => (pairs.push('"'+value[0]+'": '+value[1]), pairs), []).join(", ")+' }'), test);

test("Leopard", "Lion")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }



1> 小智..:

如果您正在处理小字符串,则无需复杂化.以下方法很简单,在处理非常大的字符串时会牺牲性能.

首先,我们需要定义一个对象来保存重复字符的计数.

然后我们需要迭代每个字符串.迭代字符串有很多方法,但在下面的例子中我要么:

Array#forEach使用Function#call方法(ES5示例)调用字符串,或

for...of循环(ES6示例).

这两种方法都有效,因为字符串是内置可迭代对象的一个​​示例.

在迭代字符串时,我们需要首先检查重复对象中是否存在该字符.

如果是,我们不需要再次检查,只需增加数字.

如果没有,则扫描另一个字符串以查看该字符是否出现在那里.

如果是,则递增该字符的重复计数器.

如果没有,什么都不做.

使用此方法,您不会计算不重复的字符,也不会尝试验证已经验证的字符.这可以通过更长的字符串提供一些显着的速度提升

完成两个字符串的迭代后,只需返回重复的holder对象即可.

ES6示例

const countLetters = (v1, v2) => {
  const out = {};
  
  for(let char of v1) (out[char] || v2.indexOf(char) !== -1) && (out[char] = (out[char] || 0) + 1);
  for(let char of v2) (out[char] || v1.indexOf(char) !== -1) && (out[char] = (out[char] || 0) + 1);

  return out;
}

const test = (v1,v2) => (console.log(JSON.stringify(countLetters(v1, v2))),test);

test("Leopard", "Lion")            // { "L": 2, "o": 2 }
    ("Hello", "World")             // { "l": 3, "o": 2 }
    ("Flower", "Power")            // { "o": 2, "w": 2, "e": 2, "r": 2 }
    ("phone", "Memphis")           // { "p": 2, "h": 2, "e": 2 }
    ("Bookkeeper", "Zookeeper")    // { "o": 4, "k": 3, "e": 6, "p": 2, "r": 2 }
    ("Some thing", "Other thing"); // { "e": 2, " ": 2, "t": 3, "h": 3, "i": 2, "n": 2, "g": 2 }
推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有