Что здесь делает цикл for?

Я пытаюсь понять этот блок кода, я видел это в классе, но до сих пор не понимаю.

Я понимаю, что и КАК работает карта. Это объект значения пары ключей. В данном случае я просто не понимаю, что происходит. Я вижу, что у нас есть char и int, но я не понимаю, как они взаимодействуют в этом случае.

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        map<char, int> mapS;
        map<char, int> mapT;

        for(int i = 0; i < s.length(); i++)
        {
            if (mapS[s[i]] != mapT[t[i]]) return false;
            
            mapS[s[i]] = i+1;
            mapT[t[i]] = i+1;
        }
        return true;
    }
};

Я попытался распечатать результаты после каждого for и получил 0 и 1 (не в двоичном смысле). Я также знаю, что мы берем символ с номером «i» + 1 и помещаем его в индекс карты. Что мне не хватает?

Спасибо!

Извините, я все еще не привык размещать здесь хорошие вопросы.

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

Сначала мы инициируем две карты (mapS и mapT).

Во-вторых, перебрать длину строки s. (Думаю, мы можем предположить, что строка t имеет одинаковую длину? Эта часть не ясна.)

Мы проверяем, равен ли символ в s[i] t[i], и он также должен существовать в карте. (вот где он разваливается для меня)

Последующую линию я рассматриваю как перспективную и добавляю ее на карту.

Затем мы возвращаемся, если у нас нет проблем.

Теперь извините меня, если я ошибаюсь, но, согласно документации, мы не должны сравнивать ключи здесь? Или вообще чего-то не хватает.

Кроме того, кто-то упомянул, понимаю ли я изоморфность. Я не совсем. У меня есть общее представление.

что именно вам не понятно? Что такое ввод? Что должен делать код? Кстати, код вызывает неопределенное поведение, когда t.length() < s.length().

463035818_is_not_a_number 11.01.2023 16:27

ключ - это символ в строке, а сопоставленное значение - это индекс + 1, где этот символ появляется в строке

463035818_is_not_a_number 11.01.2023 16:28

Лучший подход к этому вопросу: сформулируйте, что, по вашему мнению, он делает, приведите свои рассуждения и спросите, правы ли вы. Если ты прав. Холодные бобы. Вы не нуждались в нас. Если вы не правы, мы можем изучить изъян в рассуждениях и объяснить, как избежать подобных проблем в будущем. Если кто-то просто выплюнет ответ на вопрос в его текущей формулировке: «Это, гм, зацикливается». вы не узнаете почти столько же. Это также показывает, что вы приложили определенные усилия для решения проблемы, и никогда не следует недооценивать социальную значимость показа их работы.

user4581301 11.01.2023 16:28

или другой способ подойти к этому: как бы вы написали код для проверки изоморфности двух строк? Если вы понятия не имеете, если вы не понимаете, что означает изоморфность двух строк, то естественно, что вы изо всех сил пытаетесь понять этот код, это нужно сначала уточнить.

463035818_is_not_a_number 11.01.2023 16:31

Несвязанный: передайте string s, string t по const ссылке, и вы, вероятно, сэкономите компьютеру некоторую работу. Компилятор любит подсказки, которые дают понять, что данные не изменятся и что их не нужно копировать. Также давайте вещам описательные имена. Это облегчает чтение кода, и это ДЕЙСТВИТЕЛЬНО отстой, когда вы часами выясняете, что использовали i там, где должны были использовать j.

user4581301 11.01.2023 16:38

... не забудьте рассказать им о Почему «using namespace std;» считается плохой практикой?, что они должны передавать строки по ссылке, что им следует использовать неупорядоченную карту, и что не все должно быть членом класса, и, наконец, что не менее важно, что функция потенциально вызывает неопределенное поведение

463035818_is_not_a_number 11.01.2023 16:38

Мы проверяем, равен ли символ в s[i] t[i], и он также должен существовать в карте. Не совсем. Он сравнивает позицию, возвращаемую поиском символа из каждой строки в соответствующих картах. Мы проверяем, есть ли у нас одинаковая структура с разным содержимым. Мы хотим найти, например, что T и E постоянно находятся в одних и тех же местах в строке. Мы используем карту, чтобы увидеть, были ли последняя буква T и последняя буква E в одном и том же месте. Если их не было, мы нашли Т там, где не нашли Е, или наоборот, и структура не та.

user4581301 11.01.2023 17:16

@user4581301 user4581301, а поскольку ключ является символом, когда мы используем s[i] (символ, он же ключ), присваиваемым значением будет I, поэтому, если ключ имеет 2 значения, он не изоморфен? Я приближаюсь или.. Я обещаю, что делаю все возможное 😅

Wayne Miles 11.01.2023 17:23

Рекомендация: подайте две простые строки, достаточно 3-4 символов, которые, как вы знаете, изоморфны. Пройдитесь по программе с помощью отладчика строка за строкой и посмотрите, что произойдет. Сделайте то же самое с входными данными, которые, как вы знаете, не изоморфны. Как только вы овладеете простыми примерами, немного усложните ситуацию. Если вы не знаете, как использовать отладчик, я бы сказал, что это хорошие шансы, поскольку его никогда не учат в школах, вот ссылка на то, как использовать отладчик Visual Studio, который поможет вам начать.

user4581301 11.01.2023 17:26

Все современные отладчики с графическим интерфейсом практически одинаковы, просто разное расположение кнопок и формулировка. Вот немного более подробное руководство о том, как быстро перемещаться по коду

user4581301 11.01.2023 17:26

В map ключ может иметь только одно значение, но имеет значение только самое последнее. Если какое-либо из предыдущих проверенных значений не совпадало, функция обнаружила бы несовпадение и завершилась бы, поэтому вам не нужно их сохранять.

user4581301 11.01.2023 17:27

Важно знать о map: когда вы используете оператор [] для ключа, который не был сохранен, создается новая пара, сопоставляющая ключ со значением , инициализированным по умолчанию. Таким образом, mapS[s[i]], если s[i]i еще не был замечен, сопоставит s[i] с 0, а затем вернет 0. Вот почему все позиции являются i+1, чтобы избежать ложных совпадений между ключом, который на самом деле в противном случае законно был бы равен 0, и новыми ключами. Вот почему вы не можете использовать [] с константой map.

user4581301 11.01.2023 17:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
138
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сначала я пройдусь по предоставленному вами объяснению.

Сначала мы инициируем две карты (mapS и mapT).

Педантично инициализируем две карты.

Во-вторых, перебрать длину строки s. (Я думаю, мы можем предположить строка t имеет одинаковую длину? Эта часть не ясна.)

Ради этой задачи мы можем предположить, что t не меньше, чем s.

Мы проверяем, равен ли символ в s[i] t[i],

Нет. Это было бы s[i] == t[i].

и это также имеет существовать на карте. (вот где он разваливается для меня)

Нет. mapS[s[i]] равно 0, если мы никогда не сохраняли значение для s[i] в mapS.

Вот что на самом деле делает условие if:

  • Он ищет s[i] в mapS, возвращая некоторое количество int — либо int, которое мы последний раз сохраняли для ключа s[i], либо 0, если мы никогда не сохраняли int для ключа s[i].

  • Он ищет t[i] в mapT, возвращая некоторое количество int — либо int, которое мы последний раз сохраняли для ключа t[i], либо 0, если мы никогда не сохраняли int для ключа t[i].

  • Если два int различны, это заставляет функцию вернуться раньше со значением false.

После этого я рассматриваю строку как перспективную и добавляю ее в карта.

Оно не смотрит вперед. Забегая вперед, будет что-то вроде s[i+1] или t[i+1]. Он сохраняет i+1 на обеих картах, но не «заглядывает вперед».

Затем мы возвращаемся, если у нас нет проблем.

Мы возвращаем true, если выходим из тела цикла, потому что i достигла длины s, ни разу не возвращаясь раньше.

Теперь извините, если я ошибаюсь, но согласно документации разве мы не должны сравнивать ключи здесь?

Какая документация?

Кроме того, кто-то упомянул, понимаю ли я изоморфность. Я не совсем. У меня есть общее представление.

Не очевидно, что здесь означает «изоморфный», но мы с этим разберемся.

Теперь я пройдусь по моему анализу этой функции.

Один из способов понять, что делает цикл, — начать с простого ввода и пройти цикл «вручную» столько раз, сколько необходимо. (Вы начинаете с простого ввода, поэтому вам не нужно повторять его слишком много раз!)

Давайте сосредоточимся на переменной mapS и посмотрим, что с ней делает цикл. Поскольку mapS индексируется с использованием элементов из s, нам нужно выбрать некоторую строку для s. Используем "abcab".

Программа обновляет mapS только одним оператором:

        mapS[s[i]] = i+1;

Теперь давайте пройдемся по циклу и посмотрим, как развивается mapS. Предположим пока, что условие if всегда ложно.

is[i]mapS перед телом цикла mapS после тела цикла 0 'a' (пустой) ['a']=1 1 'b'['a']=1['a']=1['b']=2 2 'c'['a']=1['b']=2['a']=1['b']=2['c']=3 3 'a'['a']=1['b']=2['c']=3['a']=4['b']=2['c']=3 4 'b'['a']=4['b']=2['c']=3['a']=4['b']=5['c']=3

Итак, в начале тела цикла мы можем сказать: для каждого символа c в s.substr(0, i) (первые i символы s) mapS[c] на единицу больше, чем последний индекс c в s.substr(0, i).

Обратите внимание, что s.substr(0, i) — это пустая строка, когда i == 0.

Как я упоминал ранее, mapS возвращает 0 для любого ключа, который на самом деле не хранится на карте. Итак, давайте подумаем, что 0 означает «s.substr(0, i) не содержит c».

Имея это значение для 0, мы можем сказать, что в начале тела цикла для каждого cmapS[c] кодирует последний индекс c в s.substr(0, i) следующим образом:

  • mapS[c] == 0 кодирует значение «c не находится в s.substr(0, i)».

  • mapS[c] == x кодирует значение «x - 1 < i и s[x - 1] == c и s.substr(0, i) не содержит c с индексом большим, чем x - 1».

Поскольку mapT обновляется точно так же, как mapS, за исключением использования символов из t, мы можем сказать, что оно кодирует то же значение, что и mapS, но относительно t вместо s.

Теперь мы готовы проанализировать условие оператора if: mapS[s[i]] != mapT[t[i]].

Помните, что s.substr(0, i) не достигает s[i]; он останавливается прямо перед s[i]. То есть "abcab"[2] есть 'c', а "abcab".substr(0, 2) есть "ab"; подстрока не заканчивается на 'c'.

Итак, в условии if:

  • mapS[s[i]] — это закодированный последний индекс s[i] в s.substr(0, i). (Помните, что «закодированный последний индекс» может кодировать значение «нет такого индекса».)

  • mapT[t[i]] — это закодированный последний индекс t[i] в t.substr(0, i).

Таким образом, оператор if заставляет функцию вернуться раньше со значением false, если эти закодированные последние индексы различаются.

Теперь предположим, что цикл доходит до i == s.length(), а условие if никогда не выполняется. Затем цикл завершается нормально, и программа доказывает, что каждый раз, когда она встречала какой-то символ c в s и какой-то соответствующий символ d по тому же индексу в t, она ранее видела c и d с одинаковыми более ранними (закодированными) индексами в s и t.

Это небольшой скачок, чтобы перейти от этого к более целостному пониманию isIsomorphic, но вот что isIsomorphic(s, t) означает:

Есть ли способ единообразно сопоставить каждый символ c, который появляется в s, с уникальным символом d, который появляется в t? И наоборот? Если это так, isIsomorphic(s, t) возвращает true. В противном случае возвращается ложь.

Рассмотрим этот пример: isIsomorphic("abcab", "zyxzy"). Он возвращает true, потому что между символами, встречающимися в s, и символами, встречающимися в t, существует однородное двунаправленное сопоставление:

с т а г б у с Икс

Но isIsomorphic("abcab", "zyzzy") неверно. И 'a', и 'c' в s должны сопоставляться с 'z' в t, но 'z' в t может сопоставляться только с одним символом, а не с 'a' и 'c' одновременно.

Такое универсальное, уникальное двунаправленное отображение называется биекцией.

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

Лучшее название для этой функции может быть bijectionExists или samePattern.

Я был неправ. Моя точка зрения остается в силе. Где-то ранее ответ должен точно описывать, что хранит mapS. В обзорах кода на работе я почти всегда настаиваю на том, чтобы у каждой карты был комментарий, объясняющий ключ, значение и то, что представляет собой карта.

Mooing Duck 11.01.2023 18:43

💯 Эта функция обязательно должна иметь комментарии, если используется вне игрового контекста.

rob mayoff 11.01.2023 18:45

Спасибо! Вы также дали мне информацию о том, на что я действительно должен обращать внимание, изучая эти карты! Я ценю это. Мне придется посмотреть на оба ответа, которые я вижу здесь тонну, чтобы получить его.

Wayne Miles 12.01.2023 01:24

Вы имели в виду 'b' в 4-й строке первой таблицы?

sklott 12.01.2023 16:16

Я сделал. Спасибо, что указали на ошибку.

rob mayoff 12.01.2023 19:37
Ответ принят как подходящий

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

Две строки изоморфны, если каждому символу строки A можно сопоставить каждый символ строки B.

Например, ACAB и XCXY изоморфны.

A -> X
C -> C
B -> Y

И еще, ACAB и XCZY не изоморфны.

A -> X, Z  # 'A' cannot map to multiple characters
C -> C
B -> Y

Итак, как код решает это? Мы рассмотрим части тела функции.

std::map<char, int> mapS;
std::map<char, int> mapT;

Объявите две карты, по одной для каждой строки. Они оба пусты.

for(int i = 0; i < s.length(); i++)

Этот цикл заботится только о длине s, и мы уже столкнулись с препятствием. Если бы мы хотели проверить одностороннее отображение, где s -> t, но t !-> s, эта функция сделала бы это неправильно. Я бы пошел дальше и сказал, что если длины s и t различны, функция должна немедленно вернуть false. Потому что мы гарантированно читаем за пределы, если t.length() < s.length().

Двигаемся дальше:

{
    if (mapS[s[i]] != mapT[t[i]]) return false;

    mapS[s[i]] = i+1;
    mapT[t[i]] = i+1;
}

Мы должны взять их вместе. Проверка if просто хочет знать, имеют ли соответствующие элементы карты одинаковое значение. Если это не так, у нас есть несоответствие в нашем сопоставлении, и мы можем немедленно вернуть false. Если значения совпадают, мы обновляем обе карты вместе. i + 1 используется из-за того, как operator[] работает с картами. Если ключ не существует, попытка индексации с помощью этого ключа создаст запись на карте и инициализирует значение int равным 0. Добавление 1 — это дешевый способ обойти ложные срабатывания.

Наконец, если вы пройдете цикл без возврата false, предполагается, что строки изоморфны, и вы вернете true.

Но эта функция фиктивная. Можно почти сказать, что он проверяет «односторонний» изоморфизм, когда t длиннее, чем s, но если s длиннее, он все равно может возвращать true и при этом вызывать неопределенное поведение.

Для меня лучшим решением является сохранение фактического отображения символов. Вам также необходимо отслеживать уникальные символы во второй строке. И первая проверка должна быть на одинаковую длину.

#include <iostream>
#include <set>
#include <string>
#include <unordered_map>

class Solution {
 public:
  bool isIsomorphic(const std::string& s, const std::string& t) {
    if (s.length() != t.length()) return false;

    // The map stores the character mapping from s to t. Characters in s
    // are the key, and the value is the corresponding character in t.
    std::unordered_map<char, char> map;

    // The set stores the **unique** characters of t. This allows us to know
    // if we already have a mapping to a character in t, if it exists in
    // the set.
    std::set<char> trackedChars;

    for (std::size_t i = 0; i < s.length(); ++i) {
      char sChar = s[i];
      char tChar = t[i];

      // Have I mapped this character yet?
      if (map.find(sChar) != map.end()) {  // Yes
        // Is the mapping still correct?
        if (map[sChar] != tChar) {  // Map mismatch found
          return false;
        }
      } else {  // No
        // Has the corresponding character been mapped already?
        if (trackedChars.find(tChar) != trackedChars.end()) {
          // tChar is already mapped
          return false;
        } else {  // tChar is also not yet mapped
          map[sChar] = tChar;
          trackedChars.insert(tChar);
        }
      }
    }

    // Making it out of the loop implies every character mapped correctly.
    return true;
  }
};

int main() {
  std::string one{"ACAB"};
  std::string two{"XCXY"};

  Solution s;
  std::cout << std::boolalpha << s.isIsomorphic(one, two) << '\n';
}

Выход:

true

Не хочу нагромождать сказанное, но для класса такая структура кода — мусор. Это должна быть просто бесплатная функция или, может быть, вставленная в пространство имен. Но это выходит за рамки этого вопроса.

Я признаю, что это тонна информации, которую нужно принять. Мне нужно будет прочитать их несколько раз, чтобы понять это полностью. НО это очень помогает. Профессор был... менее чем полезен. Я даже осмелюсь сказать, что они вели меня в неправильном направлении. Спасибо!

Wayne Miles 12.01.2023 01:23

Другие вопросы по теме