Разбор примеров из книги

Книга адресована школьникам средних и старших классов, желающим испытать себя в «олимпийских схватках». Может быть полезна студентам-первокурсникам и преподавателям информатики.

Модераторы: Oleg_D, Модераторы

Re: Разбор примеров из книги

Сообщение deka47 » 24.11.2012 23:11:05

Извиняюсь, я туда еще не дошел был...
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

Re: Разбор примеров из книги

Сообщение bormant » 25.11.2012 00:04:42

Например, так:
Код: Выделить всё
var
  s: string;
  i: integer;
  p: boolean;
begin
  write('Введите слово: '); readln(s);
  p := true;
  for i := length(s) div 2 downto 1 do
    if s[i] <> s[length(s) - i + 1] then begin
      p := false; break;
    end;
  if p then writeln('Слово - палиндром')
  else writeln('Слово - НЕ палиндром');
end.
или даже так:
Код: Выделить всё
function IsPalindrom(s: string): boolean;
var
  i: integer;
begin
  IsPalindrom := true;
  for i := length(s) div 2 downto 1 do
    if s[i] <> s[length(s) - i + 1] then begin
      IsPalindrom := false; break;
    end;
end;

begin
  write('Введите слово: '); readln(s);
  if IsPalindrom(s) then writeln('Слово - палиндром')
  else writeln('Слово - НЕ палиндром');
end.
Последний раз редактировалось bormant 25.11.2012 11:26:22, всего редактировалось 1 раз.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Разбор примеров из книги

Сообщение deka47 » 25.11.2012 00:36:56

Код: Выделить всё
if s[i] <> s[length(s) - i - 1] then begin


Вы, наверное, имели ввиду
Код: Выделить всё
if s[i] <> s[length(s) - i + 1] then begin


А теперь вопрос, зачем мы делим строку на 2, мы обрезаем конец строки и средний символ?
Т.е. с слова "потоп" остается "по"? Но я не пойму логики, если так, то, к примеру слово "порок", тоже остается лишь "по", то почему он не выдает как палиндром? Это вопрос к Олегу, к решению задачи, которое предложено в книге.
Последний раз редактировалось deka47 25.11.2012 00:59:28, всего редактировалось 1 раз.
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

Re: Разбор примеров из книги

Сообщение Oleg_D » 25.11.2012 00:50:18

Если количество букв в слове НЕЧЁТНОЕ, то среднюю букву сравнивать саму с собой нет смысла.
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

Re: Разбор примеров из книги

Сообщение deka47 » 25.11.2012 00:55:26

Oleg_D писал(а):Если количество букв в слове НЕЧЁТНОЕ, то среднюю букву сравнивать саму с собой нет смысла.


Но я не пойму логики, если так, то, к примеру слово "порок", тоже остается лишь "по", то почему он не выдает как палиндром?
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

Re: Разбор примеров из книги

Сообщение Oleg_D » 25.11.2012 01:06:36

Потому, что в выражении:
Код: Выделить всё
if arg[i] <> arg[Length(arg)-i+1]
сравниваются первая и последняя буква, вторая и предпоследняя и т.д. в цикле вплоть до средней буквы (но исключая её), если длина нечётная.
Завтра утром вам это станет ясно, как белый день. :D
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

Re: Разбор примеров из книги

Сообщение deka47 » 25.11.2012 01:11:31

Oleg_D писал(а):Потому, что в выражении:
Код: Выделить всё
if arg[i] <> arg[Length(arg)-i+1]
сравниваются первая и последняя буква, вторая и предпоследняя и т.д. в цикле вплоть до средней буквы (но исключая её), если длина нечётная.
Завтра утром вам это станет ясно, как белый день. :D



Минутку, а разве команда:
Код: Выделить всё
for i:=1 to Length(arg) div 2


Не делит, к примеру, слово с 13 символом на 2 и оставляет только первых 6 символов?
Ну 13 div 2 = 6, 15 div 2 = 7. Остаются же лишь 6/7 первых символов? Также слово "потоп", разделить его на 2, то останется первых два символа "по", и после этой команды
Код: Выделить всё
if arg[i] <> arg[Length(arg)-i+1]

Здесь уже будет браться не полная длина строки (5 символов), а уже урезанная в 2 символа (после div).

У меня к вам еще один личный вопрос, вот я застреваю на таких задачках, с меня что-то будет? Потому-что я решил поступить на программирование, но не знаю получится ли что-то. Кажется что я туплю в каждой задаче и достаю вас здесь... Подскажите мне, вот сколько вы учились, чтобы понимать паскаль, как вы понимаете сейчас?
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

Re: Разбор примеров из книги

Сообщение Vadim » 25.11.2012 05:16:21

Подскажите мне, вот сколько вы учились, чтобы понимать паскаль, как вы понимаете сейчас?

Он учился всю жизнь и до сих пор учится. ;)
У каждого время и способ учёбы свои. Равняться тут на кого-либо не стоит.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Разбор примеров из книги

Сообщение Paster Fob » 25.11.2012 08:56:18

deka47 всё просто.
Вот мой вариант:
Код: Выделить всё
function checkword(var arg:string):boolean;
var i,j:integer;
begin
  checkword:=true;
  j:=0;
  for i:=length(arg) downto 1 do begin
    while j<length(arg) do begin
      inc(j);
      break;
    end;
    if arg[i]<>arg[j] then begin
      checkword:=false;
      break;
    end;
  end;
end;

var s:string;

begin
  writeln('введите слово');
  readln(s);
  if checkword(s) then
    writeln('это слово палиндром')
  else
    writeln('слово не является палиндромом');
  readln;
end.
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

Re: Разбор примеров из книги

Сообщение Oleg_D » 25.11.2012 11:50:00

deka47 писал(а):Минутку, а разве команда: for i:=1 to Length(arg) div 2 не делит, к примеру, слово с 13 символом на 2 и оставляет только первых 6 символов?

Нет, не делит. Здесь строка никак не затрагивается. Делится одно число -- длина строки -- на другое число, и получается предел для цикла.
deka47 писал(а):У меня к вам еще один личный вопрос, вот я застреваю на таких задачках, с меня что-то будет? Потому-что я решил поступить на программирование, но не знаю получится ли что-то. Кажется что я туплю в каждой задаче и достаю вас здесь... Подскажите мне, вот сколько вы учились, чтобы понимать паскаль, как вы понимаете сейчас?

Vadim писал(а):Он учился всю жизнь и до сих пор учится.

Это точно. :D У каждого свой путь, кто-то тупит, тупит, а потом прорывает его, горизонт проясняется. Что тут посоветовать? Настойчивость и упорство -- хорошо. Но порой лучше не упираться рогом в одну задачу или вопрос, пройти немного вперёд, через 3-4 дня вернуться и вновь почитать. Обычно это помогает. И ещё экспериментировать надо, пробовать разные варианты решений. По моим ощущениям "песни" можно освоить за 6-12 месяцев. Когда я учился, никаких книг по Паскалю не было, пользовались скупыми техническими руководствами.
Paster Fob писал(а):
Код: Выделить всё
    while j<length(arg) do begin
      inc(j);
      break;
    end;

Мудро накручено, не сразу понял. :D
Проще так:
Код: Выделить всё
   if j<length(arg) then Inc(j);
А ещё проще:
Код: Выделить всё
   Inc(j);
поскольку IF тоже лишний. К тому же в вашем решении цикл вдвое длиннее. Ну, опыт -- дело наживное. :)
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

Re: Разбор примеров из книги

Сообщение bormant » 25.11.2012 11:54:19

deka47 писал(а):
Код: Выделить всё
if s[i] <> s[length(s) - i - 1] then begin

Вы, наверное, имели ввиду
Код: Выделить всё
if s[i] <> s[length(s) - i + 1] then begin

абсолютно верно.
deka47 писал(а):А теперь вопрос, зачем мы делим строку на 2, мы обрезаем конец строки и средний символ?
Т.е. с слова "потоп" остается "по"? Но я не пойму логики, если так, то, к примеру слово "порок", тоже остается лишь "по", то почему он не выдает как палиндром? Это вопрос к Олегу, к решению задачи, которое предложено в книге.
Мы с самой строкой ничего не делаем. length(s) возвратит нам число -- длину строки, пусть n. Затем, мы будем проверять условие палиндромности исходя из его определения, сравнивая 1-ю букву с n-ной, 2-ю с (n-1) и т. д. То есть, сравниваем [i]-ю с [n-i+1]-ой. Как только найдем неравенство, условие палиндромности нарушено и можно завершать проверку. Перебрав индексы от 1 до (n div 2) заметим, что дальше продолжать нет смысла: при четном количестве одна половина уже полностью была сравнена с другой, а при нечетном -- остался один один символ, который сравнивать с самим собой смысла не имеет. Но если продолжить до конца -- на результат это не повлияет, просто работа будет проделана дважды: [1] с [n] и [n] с [1] и т.д.
Еще один мрмент: если прямой цикл без дублирования: от 1 до n div 2 (или наоборот), то обратный будет от n до (n+1) div 2 (или наоборот), например:
для 6: 1-3, 6-3
для 7: 1-3, 7-4
...
Последний раз редактировалось bormant 25.11.2012 13:00:11, всего редактировалось 1 раз.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Разбор примеров из книги

Сообщение Paster Fob » 25.11.2012 12:41:38

Oleg_D писал(а):
Paster Fob писал(а):
Код: Выделить всё
    while j<length(arg) do begin
      inc(j);
      break;
    end;

Мудро накручено, не сразу понял. :D
Проще так:
Код: Выделить всё
   if j<length(arg) then Inc(j);
А ещё проще:
Код: Выделить всё
   Inc(j);
поскольку IF тоже лишний. К тому же в вашем решении цикл вдвое длиннее. Ну, опыт -- дело наживное. :)


М-да,поторопился.Вот исправленный вариант.
Код: Выделить всё
function checkword(var arg:string):boolean;
var i,j:integer;
begin
  checkword:=true;
  j:=0;
  for i:=length(arg) downto length(arg) div 2 do begin
    inc(j);
  if arg[i]<>arg[j] then begin
      checkword:=false;
      break;
    end;
  end;
end;

var s:string;

begin
  writeln('введите слово');
  readln(s);
  if checkword(s) then
    writeln('это слово палиндром')
  else
    writeln('слово не является палиндромом');
  readln;
end.
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

Re: Разбор примеров из книги

Сообщение bormant » 25.11.2012 13:21:33

Paster Fob,
я переписал бы так:
Код: Выделить всё
function checkword(const arg: string): boolean;
var i, j: integer;
begin
  checkword:=true;
  j := (length(arg) + 1) div 2 + 1;
  for i := length(arg) div 2 downto 1 do begin
    if arg[i]<>arg[j] then begin
      checkword:=false; break;
    end;
    inc(j);
  end;
end;
3 отличия:
1) список параметров: переменная arg внутри функции не меняется, поэтому, если компилятор поддерживает const в списке параметров, то нет повода использовать var;
2) в цикле "for i := выражение1 to/downto выражение2 do" выражение1 вычисляется единожды, а выражение2 -- на каждой итерации цикла (если только оптимизатор не умеет выводить его независимость от тела цикла). В данном примере это несущественно, для коротких строк length(s) -- это всего лишь byte(s[0]), но вот операция деления, пусть и целочисленного, намного более ресурсоёмка.
3) в предварительном выполнении inc(j) мало смысла, начальному значению j вне цикла приходится давать значение заведо уменьшенное на 1, а это не способствует лёгкости понимания кода.

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

Добавлено спустя 15 минут 36 секунд:
Но мне более нравится другой вариант, вообще без делений и каких бы то ни было расчётов, просто идём навстречу с разных концов слова, сравнивая символы. Если встретились, то палиндром:
Код: Выделить всё
function IsPalindrom(s: string): boolean;
var
  i, j: integer;
begin
  i := 1; j := length(s);
  while (i < j) and (s[i] = s[j]) do begin
    inc(i); dec(j);
  end;
  IsPalindrom := i >= j;
end;


Добавлено спустя 28 минут 33 секунды:
deka47 писал(а):как бы сделать так, чтобы "запоминались" все местоположения?
Например, если речь идёт о показе отличающихся символов:
Код: Выделить всё
procedure ShowPalDiff(const s: string; MarkSame, MarkDiff: char);
var
  i, j: integer;
  p: boolean;
begin
  writeln(s);
  j := length(s); p := true;
  for i := 1 to length(s) do begin
    if s[i] = s[j] then write(MarkSame)
    else begin
      write(MarkDiff); p := false;
    end;
    dec(j);
  end;
  if p then writeln(^M'Это палиндром.', '':length(s)-14)
  else begin
    writeln; writeln('Это НЕ палиндром.');
  end;
end;

var
  s: string;
begin
  write('Введите слово: '); readln(s);
  ShowPalDiff(s, '_', '^');
end.
И прогоны:
Код: Выделить всё
Введите слово: pascal
pascal
^_^^_^
Это НЕ палиндром.

Введите слово: potop
potop
Это палиндром.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Разбор примеров из книги

Сообщение deka47 » 25.11.2012 17:54:54

Разобрался со всеми вариантами задачи, с inc вообще гениально, как по мне! Спасибо за помощь.

bormant писал(а):Перебрав индексы от 1 до (n div 2) заметим, что дальше продолжать нет смысла: при четном количестве одна половина уже полностью была сравнена с другой, а при нечетном -- остался один один символ, который сравнивать с самим собой смысла не имеет.


Т.е. мы отсекаем первую часть "строки". Мы сравнивамем 1 с последним, 2 с последним минус 1, 3 с последним минус 2. А потом уже нету смысла сравнивать последний минус 1 с вторым? Выходит слово "потоп", мы оставляем "оп" и сравниваем: "п"="п", "о"="о", верно? Буква "т" уже не затрагивается, потому что нету разницы какая она. Я верно понял?
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

Re: Разбор примеров из книги

Сообщение bormant » 25.11.2012 18:58:35

deka47 писал(а):Т.е. мы отсекаем первую часть "строки". Мы сравнивамем 1 с последним, 2 с последним минус 1, 3 с последним минус 2. А потом уже нету смысла сравнивать последний минус 1 с вторым? Выходит слово "потоп", мы оставляем "оп" и сравниваем: "п"="п", "о"="о", верно? Буква "т" уже не затрагивается, потому что нету разницы какая она. Я верно понял?
Да. Только "отсекаем" -- это в переносном смысле, строку мы никак не модифицируем, только берём из неё символы по индексу. Дело в чём, если мы в качестве инструмента выбрали по каким-то причинам цикл for, мы должны указать начальное и конечное значения счётчика цикла. Счётчиком нам достаточно пробежать только половину, посколько вторую половину мы будем получать "зеркально" от противоположного конца строки.

В варианте с циклом while и парой индексов ничего предварительно рассчитывать не приходится, достаточно тривиального контроля "встречи" индексов на каждой итерации. Кроме того его возможно легко модифицировать под расширенные условия, например, на проверку "палиндромности" без учёта пробелов и знаков препинания, как в фразе "а роза упала на лапу азора".
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Пред.След.

Вернуться в Книга "Песни о Паскале"

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

Рейтинг@Mail.ru