Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   Задачка на сообразительность (https://javascript.ru/forum/offtopic/15269-zadachka-na-soobrazitelnost.html)

Matre 19.02.2011 18:10

Задачка на сообразительность
 
Написать функции для поиска подстроки в строке (аналог String.prototype.indexOf) и разделения строки на массив по разделителю (аналог String.prototype.split). Встроенные функции использовать запрещено, за исключением String.prototype.charAt. Использовать только один цикл.

with-love-from-siberia 19.02.2011 18:17

А сами Вы уже не можете? Вон и звание у Вас - Профессор.

Matre 19.02.2011 18:26

Могу. Только какой смысл сразу выкладывать всё.

Gvozd 19.02.2011 18:58

а в чем смысл-то?
это задачи не на сообразительность

Kolyaj 19.02.2011 19:02

Это задачи первого курса специализированного факультета.

Matre 19.02.2011 19:13

О да, умные люди из интернета опять меня сделали. Мешок вам на голову и в гараж.

Интересно получается: я выкладываю задачу, дебил не может её решить, говорит мне, что это я дебил и в итоге я и правда с точки зрения остальных выгляжу дебилом. Обратите внимание, что я никого конкретно не назвал дебилом, так что не сочтите за грубость.

P. S. я уже неделю не пью рисперидон!

with-love-from-siberia 19.02.2011 19:38

Это типовая задача для студента-прикладника. Здесь нет и намека на задачу на сообразительность.

Matre 19.02.2011 19:48

Повторю свою вторую любимую цитату (конечно, после гаражей и мешка на голове).

Это просто слова. Я тоже могу говорить слова. А вы сделайте что-нибудь.

Vulkan 19.02.2011 20:25

Задачки то конечно не на сообразительность, а на лёгкий экзамен. Быстренько состряпал, возможно что-то не учёл, но результаты не отличаются от стандартных функций:
function TestSplit(string, sep) {
    var words = [],
        word = '',
        char = '';
    string = string + sep;
    for (i = 0; i < string.length; i++) {
        char = string.charAt(i);
        if (char == sep) {
            words.push(word);
            word = '';
        } else {
            word += char;
        }
    }
    return words;
}

alert((TestSplit('1 2 3 4 5', ' ')).join(' - ')); // мой вариант
alert(('1 2 3 4 5'.split(' ')).join(' - ')); // стандартный вариант


function TestIndexOf(string, s) {
    var char = '';
    for (i = 0; i < string.length; i++) {
        char = string.charAt(i);
        if (char == s) {
            return i;
        }
    }
}

alert(TestIndexOf('1 2 3 4 5', '4')); // мой вариант
alert('1 2 3 4 5'.indexOf('4')); // стандартный вариант

Kolyaj 19.02.2011 20:54

Vulkan,
параметры sep и s могут быть произвольной длины.

Matre 19.02.2011 21:17

Vulkan

Вот как, например, должна выглядеть вторая функция:

String.prototype.index = function (str) {
	var j = -1, c = false, L1 = this.length, L2 = str.length - 1;
	for (var i = 0; i < L1; i++) {
		if (str.charAt(0) === this.charAt(i))
			c = true, j = i;
		if (c && i === j + L2)
			return j;
		if (c && str.charAt(i) != this.charAt(i + j))
			c = false;
	}
	return j;
};


К своему стыду могу сказать, что эта задача потребовала от меня много усилий, это наверно из-за рисперидона. Оставляю Вам возможность решить первую задачу и вернуть звание дурачка обратно мне.

monolithed 19.02.2011 21:28

Зачем все так усложнять? ;)
String.prototype.sepa = function(str, sep) {
     return this.replace(new RegExp(str, 'g'), sep);
};

alert('1 2 3 4 5'.sepa(' ', '-'));


Цитата:

Сообщение от Matre
Вот как, например, должна выглядеть вторая функция:

String.prototype.index = function(str) {
    return [this].join().search(str);
};

alert('1 2 3 4 5'.index('4'));

B@rmaley.e><e 19.02.2011 21:33

Цитата:

Сообщение от Matre
Вот как, например, должна выглядеть вторая функция:

Это функция
Цитата:

Сообщение от Matre
разделения строки на массив по разделителю (аналог String.prototype.split)

? В то же время это ни разу не аналог String.prototype.indexOf
String.prototype.index = function (str) {
	var j = -1, c = false, L1 = this.length, L2 = str.length - 1;
	for (var i = 0; i < L1; i++) {
		if (str.charAt(0) === this.charAt(i))
			c = true, j = i;
		if (c && i === j + L2)
			return j;
		if (c && str.charAt(i) != this.charAt(i + j))
			c = false;
	}
	return j;
};

var s1 = 'aababaO_o',
     l1 = 'aba', l2 = 'abac', l3 = 'aa', l4 = 'O_o';

alert( [
 [s1.index(l1), s1.indexOf(l1)],
 [s1.index(l2), s1.indexOf(l2)],
 [s1.index(l3), s1.indexOf(l3)],
 [s1.index(l4), s1.indexOf(l4)]
].join('\n') )

Matre 20.02.2011 10:58

monolithed

Цитата:

Встроенные функции использовать запрещено
B@rmaley.e><e

Всё ведь работает.

Vulkan 20.02.2011 11:06

По поводу своих вариантов - и правда забыл что в качестве параметров могут ещё и строки из нескольких символов передаваться, как время будет исправлю.
Matre, результаты работы твоей функции отличаются от результатов работы встроенной.

B@rmaley.e><e 20.02.2011 11:31

Цитата:

Сообщение от Matre
Всё ведь работает.

Где работает? Результаты совпали только в одном случае.

Matre 20.02.2011 12:10

Цитата:

результаты работы твоей функции отличаются от результатов работы встроенной.
Цитата:

Где работает? Результаты совпали только в одном случае.
Не понял... Вы это имеете ввиду?

B@rmaley.e><e 20.02.2011 12:50

Это.

Matre 20.02.2011 13:14

Какой-то чудной у Вас браузер.

рони 20.02.2011 14:32

Вариант...
String.prototype.index = function (b) {
    for (var a = -1, d = this.length, c = 0; c < d; c++) {
        if (b.length > d) break;
        a++;
        a = b.charAt(a) === this.charAt(c) ? a : b.charAt(0) === this.charAt(c) ? 0 : -1;
        if (a == b.length - 1) return c - b.length + 1
    }
    return a
};
var s1 = 'aababaO_o',
     l1 = 'aba', l2 = 'abac', l3 = 'aa', l4 = 'O_o';
alert( [
 [s1.index(l1), s1.indexOf(l1)],
 [s1.index(l2), s1.indexOf(l2)],
 [s1.index(l3), s1.indexOf(l3)],
 [s1.index(l4), s1.indexOf(l4)]
].join('\n') )

with-love-from-siberia 20.02.2011 14:36

Matre,
Дело не в браузерах, дело в алгоритме. Он работает неверно. Проверьте на любых примерах. Например:
String.prototype.index = function (str) {
	var j = -1, c = false, L1 = this.length, L2 = str.length - 1;
	for (var i = 0; i < L1; i++) {
		if (str.charAt(0) === this.charAt(i))
			c = true, j = i;
		if (c && i === j + L2)
			return j;
		if (c && str.charAt(i) != this.charAt(i + j))
			c = false;
	}
	return j;
};

// Где ищем
var s = 'A';
// Что ищем
var t = 'AB';

alert([
	'В строке "' + s + '"', 
	'ищем "' + t + '"', 
	'и находим: ' + s.index(t), // Должно быть -1, показывает 0
	'хотя там быть не должно: ' + s.indexOf(t) // Показывает -1
].join('\n'));

B@rmaley.e><e 20.02.2011 14:56

рони,
String.prototype.index = function (c) {
    for (var a = -1, d = this.length, b = 0; b < d; b++) {
        if(c.length > d) return a;
        a++;
        a = c.charAt(a) === this.charAt(b) ? a : c.charAt(0) === this.charAt(b) ? 0 : -1;
        if (a == c.length - 1) return b - c.length + 1
    }
    return a
};
var s1 = 'aababaO_o',
     l1 = 'abaO', l2 = 'abac', l3 = 'aa', l4 = 'O_o';
alert( [
 [s1.index(l1), s1.indexOf(l1)]
].join('\n') )


Matre, вполне нормальный. Причем точно такие же результаты будут у других пользователей.
А вот алгоритм у Вас кривой.

Принимая во внимание то, что все известные алгоритмы поиска подстроки требуют либо предварительной обработки данных, либо работают дольше, чем за O(n), маловероятно, что существует алгоритм, позволяющий "с места", без подготовки найти заданную подстроку в строке за один просмотр исходной строки.

Gvozd 20.02.2011 15:19

Цитата:

Сообщение от B@rmaley.e><e
Принимая во внимание то, что все известные алгоритмы поиска подстроки требуют либо предварительной обработки данных, либо работают дольше, чем за O(n), маловероятно, что существует алгоритм, позволяющий "с места", без подготовки найти заданную подстроку в строке за один просмотр исходной строки.

http://algolist.manual.ru/search/esearch/kmp.php
Работает за линию от входных данных.
Но условие задачи было не обеспечить определенную сложность алгоритма, а использовать только один цикл.
Сделать из группы вложенных циклов, или из группы подряд идущих циклов, один единственный - вполне решаемая задача, хотя и из разряда "никогда так не делай"

Sweet 20.02.2011 15:48

Я бы реализовал вот так:
String.prototype.index = function(x){
  var i1, i2, l1 = this.length, l2 = x.length;

  for(i1 = 0; i1 < l1; i1++)
    if(this[i1] === x[0])
      for(i2 = 0; i2 <= l2; i2++){
        if(i2 === l2) return i1;
        if(this[i1 + i2] === x[i2]) continue;
        break;
      };
  return -1;
};

var s1 = 'aababaO_o', 
    l1 = 'abaO', l2 = 'abac', l3 = 'aa', l4 = 'O_o'; 
alert( [ 
  [s1.index(l1), s1.indexOf(l1)],
  [s1.index(l2), s1.indexOf(l2)],
  [s1.index(l3), s1.indexOf(l3)],
  [s1.index(l4), s1.indexOf(l4)] 
].join('\n') );

рони 20.02.2011 16:11

Ещё вариант ...
String.prototype.index = function (c) {
    for (var a = -1, d = this.length, b = 0; b < d; b++) {
        if (c.length > d) break;
        a++;
        if (c.charAt(a) !== this.charAt(b)) {
            b -= a;
            a = -1
        }
        if (a == c.length - 1) return b - c.length + 1
    }
    return a
};
var s1 = 'aababaO_o',
     l1 = 'abaO', l2 = 'abac', l3 = 'aa', l4 = 'O_o';
alert( [
 [s1.index(l1), s1.indexOf(l1)]
].join('\n') )

// Где ищем
var s = 'A';
// Что ищем
var t = 'AB';

alert([
	'В строке "' + s + '"',
	'ищем "' + t + '"',
	'и находим: ' + s.index(t), // Должно быть -1, показывает 0
	'хотя там быть не должно: ' + s.indexOf(t) // Показывает -1
].join('\n'));

var s1 = 'aababaO_o',
     l1 = 'aba', l2 = 'abac', l3 = 'aa', l4 = 'O_o';

alert( [
 [s1.index(l1), s1.indexOf(l1)],
 [s1.index(l2), s1.indexOf(l2)],
 [s1.index(l3), s1.indexOf(l3)],
 [s1.index(l4), s1.indexOf(l4)]
].join('\n') )

Vulkan 20.02.2011 16:18

Sweet, можно только один цикл использовать.

Sweet 20.02.2011 16:25

Цитата:

Сообщение от Vulkan
можно только один цикл использовать.

А мне захотелось использовать два!:) Хотя, конечно, можно еще было бы вместо for воспользоваться while, или вообще без циклов...

B@rmaley.e><e 20.02.2011 16:27

Цитата:

Сообщение от Gvozd
Сделать из группы вложенных циклов, или из группы подряд идущих циклов, один единственный - вполне решаемая задача

Как Вы собрались объединять, например, это?
var arr = [3,4,5,6,7,8,9,0,1 /* and so on */], s = 0;
for(var i =0, length = arr.length; i < length; ++i){ // предварительные вычисления
  s += arr[i];
}

for(var i = 0, length = arr.length; i < length; ++i){ // самое то.
  arr[i] = (arr[i] / s * 100).toFixed(2) + '%';
}
alert([s, arr].join('\n'));

Сомневаюсь, что КМП можно записать одним циклом.

P.S. Грязные "хаки" вида
function indexOf(haystack, needle){
  var n = haystack.length,
       m = needle.length;
  var i = 0, j = 0, offset = 0;
  for(var k = 0, length = n * m; k < length; ++k){
    i = k / m | 0;
    j = k % m;
    if(haystack[i + offset] != needle[j]){
      offset = 0;
      k += m - j - 1;
    } else {
      ++offset;
      if(j == m - 1) return i;
    }
  }
  return -1;
}

var s1 = 'aababaO_o',  
    l1 = 'abaO', l2 = 'abac', l3 = 'aa', l4 = 'O_o';  
alert( [  
  [indexOf(s1, l1), s1.indexOf(l1)],
  [indexOf(s1, l2), s1.indexOf(l2)],
  [indexOf(s1, l3), s1.indexOf(l3)],
  [indexOf(s1, l4), s1.indexOf(l4)],
  [indexOf(s1, 'a'), s1.indexOf('a')]
].join('\n') );
не рассматриваем.

with-love-from-siberia 20.02.2011 17:02

Эта задача не требует хаков, или дополнительных циклов. Задача действительно решается в один цикл. Не надо городить огород:
String.prototype.hasAt = function(needle, pos)
{
	var i = Math.min(this.length, Math.max(0, Number(pos) || 0));
	var j;
	var found;
	for ( ; i < this.length; i++) {
		if ( found ) {
			if ( i - j == needle.length ) {
				return j;
			}
			found = this.charAt(i) == needle.charAt(i - j);
		} else {
			found = this.charAt(i) == needle.charAt(0);
			if ( found ) {
				j = i;
			}
		}
	}
	return found && this.length - j == needle.length 
		? j 
		: -1;
};

Для "чистоты" эксперимента следовало бы заменить Math.min/Math.max соответствующими проверочными условиями, чтобы позиция начала поиска не выходила за пределы строки. Но это не существенно.

B@rmaley.e><e 20.02.2011 17:34

String.prototype.hasAt = function(needle, pos)
{
	var i = Math.min(this.length, Math.max(0, Number(pos) || 0));
	var j;
	var found;
	for ( ; i < this.length; i++) {
		if ( found ) {
			if ( i - j == needle.length ) {
				return j;
			}
			found = this.charAt(i) == needle.charAt(i - j);
		} else {
			found = this.charAt(i) == needle.charAt(0);
			if ( found ) {
				j = i;
			}
		}
	}
	return found && this.length - j == needle.length 
		? j 
		: -1;
};

var s1 = 'aababababaO_o',   
    l1 = 'ababaO';   
alert( [
  [s1.hasAt(l1), s1.indexOf(l1)] 
].join('\n') );


Кто еще верит, что все так просто?

with-love-from-siberia 20.02.2011 17:38

B@rmaley.e><e,
Верно подмечено. (-:

Sweet 20.02.2011 18:06

Цитата:

Сообщение от with-love-from-siberia
Задача действительно решается в один цикл

Почему???
Это явный бред! Или может здесь кто-то панически боится циклов и микробов?
Цитата:

Сообщение от B@rmaley.e><e
Кто еще верит, что все так просто?

В моем варианте все верно, хотя он предельно прост, так что я - верю!:agree:

Vulkan 20.02.2011 18:23

Sweet, переделай в своём варианте второй цикл например под рекурсию и он будет соответствовать всем условиям =))

B@rmaley.e><e 20.02.2011 19:00

Sweet, таково условие - один цикл.
Vulkan, это все ерунда. Под одним циклом подразумевалась сложность O(n) (простой цикл по всем элементам строки). А поиск лазеек и дыр в правилах оставим юристам.

Sweet 20.02.2011 19:26

Ой, извините! Я думал про один цикл - это не изначальное условие.
Впрочем, есть не один способ перебора без цикла, например, вот (внизу). Или вот, например:
String.prototype.index = function(x){  
  var string = this, l1 = this.length, l2 = x.length;

  for(var i = 0; i < l1; i++) if(this[i] === x[0] && (function(){
    var count = 0;
  
    return (function(){
      if( count === l2 ) return true;
      if( string[i + count] === x[count++] ) return arguments.callee();
      return false;
    })();
    
  }())) return i;
  return -1;
};

Vulkan 20.02.2011 19:30

Sweet,
Цитата:

Сообщение от B@rmaley.e><e (Сообщение 93341)
Под одним циклом подразумевалась сложность O(n) (простой цикл по всем элементам строки). А поиск лазеек и дыр в правилах оставим юристам.


with-love-from-siberia 20.02.2011 21:47

Продолжаю утверждать, что это простой алгоритм, который не требует особых хаков в стиле JS и который решается одним циклом.

Пусть даны СТРОКА1 и СТРОКА2. Найти позицию вхождения СТРОКА2 в СТРОКА1

Выполните цикл не более чем i < (длина СТРОКА1 - (длина СТРОКА2 / 2))
Сравнивайте начальный (0) и конечный символы (длина СТРОКА2 - 1) из СТРОКА2 с текущими позициями в СТРОКА1 (i) и (i + длина СТРОКА2 - 1), соответственно. Повторяйте сравнение.

float 20.02.2011 21:59

Т.к. теоретически точка начала совпадений мот быть в любой(каждой) позиции простым циклом нельзя(без встроенных функций[любых]).
Мой вариант:
String.prototype.at = function(str) {
	var tl = this.length, sl = str.length;
	if(tl >= sl) {
		for(var i=0, j=0; i<tl; i++) {
			if(this[i] == str[j]) {
				++j; if(sl == j) {return ++i-j;}
			}
			else{
				if(this[i] == str[0]) {j=1;}
				else {i-=j; j=0;}
			}
		}
		return -1;
	}
	else {
		return -1;
	}
}

var s1 = 'aababababaO_o', l1 = 'ababaO', l2 = 'abac', l3 = 'aa', l4 = 'O_o';

alert( [
	[s1.at(l1), s1.indexOf(l1)],
	[s1.at(l2), s1.indexOf(l2)],
	[s1.at(l3), s1.indexOf(l3)],
	[s1.at(l4), s1.indexOf(l4)]
].join('\n') );

with-love-from-siberia 20.02.2011 22:17

Цитата:

Сообщение от float
Т.к. теоретически точка начала совпадений мот быть в любой(каждой) позиции простым циклом нельзя(без встроенных функций[любых]).

Раскройте смысл сказанного? То что Вы написали здесь противоречит Вашему же примеру.

float 20.02.2011 22:20

Цитата:

То что Вы написали здесь противоречит Вашему же примеру.
Не противоречит, т.к. в примере итераций больше чем букв в строке. Line 10


Часовой пояс GMT +3, время: 11:07.