быстрый способ узнать, имеют ли две строки общий символ
у меня есть различные алгоритмы сравнения строк и различий, но в какой-то момент, прежде чем применять их, я хочу найти, имеют ли две строки хотя бы один общий символ. Таким образом я смогу пропустить более сложные функции. Поэтому мне нужна очень быстрая функция в JavaScript, которая найдет, имеет ли строка A и строка B хотя бы один общий символ.
сначала я думал создать карту символов для строки A, а затем проверить каждый символ в строке B против этой карты, пока что-то найдено. Но потом я понял, что если обе строки огромны и имеют общий первый символ, это будет неэффективно для создания полной карты для строки A.
UPDATE: кто-то ответил с помощью indexOf(), это меня смущает. Может быть, фраза "иметь общий символ" означает то же самое, что и "строка-это подстрока другого"? Позвольте мне привести пример того, чего я хочу:
JavaScript
и Stop and stay
символ S
В общем. Другой пример будь please look right
и break the ice
у них есть символ k
В общем. 9 ответов
в одну сторону,
фактически расширение вашего подхода к карте
создать два bit Arrays
для вашего набора символов, скажем, для a-z
создать bit array of 26
, и какой бы характер вы ни столкнулись с вами set the flag to 1
.
Итак, Вы читаете string A
по одному символу за раз и lookup in string B's flagArray
чтобы увидеть, включен ли соответствующий бит, (иначе установите этот бит как " on " в флаге строки A), в той же итерации сделайте это для текущего символа строки B и флагарая A, если ни один из них не соответствует, затем установите соответствующие биты для текущего символа в обоих bitarrays
вы можете создать кэш проверенных символов, если вы беспокоитесь о (вероятно, минимальных) накладных расходах на поиск одного и того же символа дважды. Что-то вроде этой функции может удовлетворить законопроект:
var common_char = function(str1, str2) {
var map = {};
return Array.prototype.some.call(str1, function(c) {
if( !map[c] && str2.indexOf(c) >= 0 ) {//character c not checked and in str2
return true;
}
map[c] = true;
});
}
самый простой метод - перебрать каждую букву в одной строке и посмотреть, содержит ли другая какая-либо из одиночных букв.
вы не можете получить более эффективно, чем перейти к каждой букве, если строки не отсортированы в алфавитном порядке для начала.
function anythingInCommon(a, b){
if( b.length < a.length )
return anythingInCommon(b, a)
for( var i = 0, len = a.length; i < len; i++ )
if(b.indexOf(a[i]) != -1)
return true;
return false
}
console.log(
anythingInCommon("aaaaaaaaaaaaaabbbbbbccccc", "xc"),
anythingInCommon("aaaaaaaaaaaaaabbbbbbccccc", "x")
)
anythingInCommon('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','cddabddde')
скрипач:http://jsfiddle.net/LRxGK/4/
построить дерево суффиксов или использовать динамическое программирование может решить эту проблему
Вижу самая длинная общая проблема подстроки, это может быть полезно.
строки работают как массивы, мы хотим имитировать set.intersect
в javascript, чтобы получить общие буквы обоих контейнеров, так как JS не имеет собственных наборов, вы можете проверить этот ответ:
самый простой код для пересечения массива в javascript
добавление других ответов, для быстрого способа сделать это быстро и грязно если строки уже отсортированы:
проверьте первую букву в каждой отсортированной строке, если они равны. честная выхода
pop самый низкий из двух первых символов
если какая-либо из строк пуста, верните false
Гото 1.
все будут кричать на меня за использование regexp, но это делает жизнь SOOOOOO простой
var str1 = 'Stop and stay'
, str2 = 'JavaScript'.replace(/(.)(?=.*)|\s/g, "")
// replace(): removes space & repeated characters
, reg = new RegExp("["+ str2 +"]", 'g')
// global flag to return a list of matched characters
;
console.log(str1.match(reg));
// array('S', 't', 'a', 'p', 't', 'a')
этот ответ опирается на ответы gaurav5430 и megawac.
этот алгоритм создает набор, содержащий каждый символ, который появился в любой из строк.
- он работает в O (n)
- он не требует, чтобы строки сортировались
- он рассматривает обе строки одновременно
- он завершится, как только он достигнет точки, где любой символ существует в обеих строках, даже если этот символ является первым характер.
- здесь пример JSFiddle.
function common_char(left, right) {
var left_map = {};
var right_map = {};
var index = 0;
while (index < left.length || index < right.length) {
// Check left array
if (index < left.length) {
var c = left[index];
left_map[c] = true;
// Check if it exists in the other map
if (right_map[c]) {
return true;
}
}
// Check right array
if (index < right.length) {
var c = right[index];
right_map[c] = true;
// Check if it exists in the other map
if (left_map[c]) {
return true;
}
}
index++;
}
return false;
}
Если вы ищете элементы в контейнерах, где элементы могут содержать произвольные значения, то, поскольку контейнеры не сортируются, вы вряд ли сможете сделать это быстрее, чем выполнять линейный поиск O(n^2), например, используя два вложенных цикла for для итерации по элементам в строках. Как вы, несомненно, заметили, это будет довольно медленно в JavaScript.
однако символы обычно являются частью ограниченного набора (например, 128 символов для ASCII). Так что это может стоить при создании двух карт, по одной для каждой строки, и вставлять символы из строк в соответствующие карты, как вы делаете поиск. Таким образом, вы можете проверить, был ли символ ранее замечен на карте строк, прежде чем сравнивать следующий символ для совпадения. В зависимости от вашей типичной длины строки это может быть быстрее, чем простой линейный поиск.
предварительная сортировка строк полностью перед поиском (например, с помощью карт) также может быть опцией, если вы будете сравнивать одну и ту же строку с другими строками несколько раз, потому что тогда начальная стоимость сортировки будет амортизирована при последующем поиске.
придумал собственное решение, идея состоит в том, чтобы сравнить с самой большой картой на данный момент. Это похоже на подход Густава Бертрама, но адаптивно выбирает, какой символ сравнивать дальше, из строки A или из строки B.
function haveCommonChar(a, b) {
var mapa = [], mapb = [], mappeda = 0, mappedb = 0, x = 0, y = 0
while(x < a.length && y < b.length) { // smart part, while both strings still have more chars
if (mappeda >= mappedb) { //compare against the largest map
// one way to speed up this part even further, will be to work in chunks of fixed size larger than 1(now it's 1)
var c = b.charCodeAt(y++)
if (mapa[c] === 1) return true
if (mapb[c] !== 1) mapb[c] = 1, mappedb++
} else {
var c = a.charCodeAt(x++)
if (mapb[c] === 1) return true
if (mapa[c] !== 1) mapa[c] = 1, mappeda++
}
}
//quickly finish the remaining chars
while(x < a.length) if (mapb[a.charCodeAt(x++)] !== undefined) return true
while(y < b.length) if (mapa[b.charCodeAt(y++)] !== undefined) return true
return false
}