我怎样才能编写一个比较两个字符串的函数,并返回一个包含两个字符串中出现的字符的对象,以及它们出现的次数?
这是一些示例输入和输出:
"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对象即可.
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 }
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 }
如果您正在处理小字符串,则无需复杂化.以下方法很简单,在处理非常大的字符串时会牺牲性能.
首先,我们需要定义一个对象来保存重复字符的计数.
然后我们需要迭代每个字符串.迭代字符串有很多方法,但在下面的例子中我要么:
Array#forEach
使用Function#call
方法(ES5示例)调用字符串,或
带for...of
循环(ES6示例).
这两种方法都有效,因为字符串是内置可迭代对象的一个示例.
在迭代字符串时,我们需要首先检查重复对象中是否存在该字符.
如果是,我们不需要再次检查,只需增加数字.
如果没有,则扫描另一个字符串以查看该字符是否出现在那里.
如果是,则递增该字符的重复计数器.
如果没有,什么都不做.
使用此方法,您不会计算不重复的字符,也不会尝试验证已经验证的字符.这可以通过更长的字符串提供一些显着的速度提升
完成两个字符串的迭代后,只需返回重复的holder对象即可.
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 }