Подстроки сравнения равной длины с использованием хеширования - PullRequest
2 голосов
/ 16 апреля 2020

В моем назначении для строки S мне нужно сравнить две подстроки одинаковой длины. Выходные данные должны быть "Yes", если они равны, "No", если они не равны. Мне даны начальные индексы двух подстрок (a и b) и длина подстрок L.

Например, для S = "Hello", a = 1, b = 3 , L = 2, подстроки: substring1 = "el" и substring2 = "lo", которые не равны, поэтому ответ будет "No".

Я думаю, хэширование каждой подстроки основной строки S и записать их все в память было бы хорошим подходом. Вот код, который я написал для этого (я попытался реализовать то, что узнал об этом из курса Coursera, который я проходил):

Эта функция принимает любую строку и значения для p и x для хеширования, и выполняет полиномиальное ха sh для данной строки.

long long PolyHash(string str, long long p, int x){
    long long res = 0;
    for(int i = str.length() - 1; i > -1; i--){
        res = (res * x + (str[i] - 'a' + 1)) % p;
    }
    return res;
}

Функция ниже просто предварительно вычисляет все хэши и заполняет массив с именем ah, который инициализируется в основная функция. Массив ah состоит из n = string length строк и n = string length столбцов (половина из которых теряется, потому что я не мог найти, как правильно заставить его работать как треугольник, поэтому мне пришлось go для полного прямоугольника angular массив). Предполагая, что n = 7, тогда ah[0]-ah[6] являются значениями ha sh для string[0]-string[6] (что означает все подстроки длины 1). ah[7]-ah[12] - это значения ha sh для string[0-1]-string[5-6] (что означает все подстроки длины 2) и т. Д. c. до конца.

void PreComputeAllHashes(string str, int len, long long p, int x, long long* ah){
    int n = str.length();
    string S = str.substr(n - len, len);
    ah[len * n + n - len] = PolyHash(S, p, x);
    long long y = 1;
    for(int _ = 0; _ < len; _++){
        y = (y * x) % p;
    }
    for(int i = n - len - 1; i > -1; i--){
        ah[n * len + i] = (x * ah[n * len + i + 1] + (str[i] - 'a' + 1) - y * (str[i + len] - 'a' + 1)) % p;
    }
}

А ниже находится основная функция. Я взял p равным какому-то большому простому числу, а x - как какое-то «случайное» простое число, выбранное вручную. Я беру текст в качестве ввода, инициализирую массив ha sh, заполняю массив ha sh, а затем беру запросы в качестве ввода, чтобы ответить на все запросы из моего массива.

int main(){
    long long p = 1e9 + 9;
    int x = 78623;
    string text;
    cin >> text;
    long long* allhashes = new long long[text.length() * text.length()];
    for(int i = 1; i <= text.length(); i++){
        PreComputeAllHashes(text, i, p, x, allhashes);
    }
    int queries;
    cin >> queries;
    int a, b, l;
    for(int _ = 0; _ < queries; _++){
        cin >> a >> b >> l;
        if(a == b){
            cout << "Yes" << endl;
        }else{
            cout << ((allhashes[l * text.length() + a] == allhashes[l * text.length() + b]) ? "Yes" : "No") << endl;
        }
    }
    return 0;
}

Однако один из контрольные примеры для этого задания на Coursera выдают ошибку вроде этого:

Failed case #7/14: unknown signal 6 (Time used: 0.00/1.00, memory used: 29396992/536870912.)

Что, я посмотрел в Интернете, означает следующее:

Unknown signal 6 (or 7, or 8, or 11, or some other).This happens when your program crashes. It can be
because of division by zero, accessing memory outside of the array bounds, using uninitialized
variables, too deep recursion that triggers stack overflow, sorting with contradictory comparator,
removing elements from an empty data structure, trying to allocate too much memory, and many other
reasons. Look at your code and think about all those possibilities.

И я весь день просматривал свой код и до сих пор не смог найти решение этой ошибки. Любая помощь, чтобы исправить это, была бы признательна.

Редактировать: Назначение заявляет, что длина входной строки может быть до 500000 символов, а количество запросов может быть до 100000. У этой задачи также есть 1 second ограничение по времени, которое довольно мало для просмотра символов по одному для каждой строки.

Ответы [ 2 ]

1 голос
/ 17 апреля 2020

Итак, я провел некоторое исследование относительно того, как я могу уменьшить сложность этого алгоритма, который я реализовал, и наконец нашел его! Оказывается, существует очень простой способ (ну, если не считать теорию, которая стоит за этим), чтобы получить значение ha sh любой подстроки, учитывая префиксы хэшей исходной строки!

Вы можете подробнее об этом здесь , но я попытаюсь объяснить это кратко.

Так что же нам делать - мы предварительно вычислили все значения ha sh для префикс-подстрок. Подстроки префикса для строки "hello" будут иметь следующий вид:

h
he
hel
hell
hello

Как только у нас будет sh значения всех этих подстрок префикса, мы можем собрать их в векторе, так что:

h[str] = str[0] + str[1] * P + str[2] * P^2 + str[3] * P^3 + ... + str[N] * P^N

где P - любое простое число (я выбрал p = 263). Затем нам нужно высокое значение, по которому мы будем принимать все по модулю, просто чтобы вещи не были слишком большими. Это число я выберу m = 10^9 + 9.

Сначала я создаю вектор для хранения предварительно рассчитанных степеней P:

vector<long long> p_pow (s.length());
p_pow[0] = 1;
for(size_t i=1; i<p_pow.size(); ++i){
    p_pow[i] = (m + (p_pow[i-1] * p) % m) % m;
}

Затем вычисляю вектор га sh значения для префиксных подстрок:

vector<long long> h (s.length());
for (size_t i=0; i<s.length(); ++i){
    h[i] = (m + (s[i] - 'a' + 1) * p_pow[i] % m) % m;
    if(i){
        h[i] = (m + (h[i] + h[i-1]) % m) % m;
    }
}

Предположим, у меня есть q запросов, каждый из которых состоит из 3 целых чисел: a, b и L.

To проверьте равенство для подстрок s1 = str[a...a+l-1] и s2 = str[b...b+l-1], я могу сравнить значения ha sh этих подстрок. И чтобы получить значение ha sh для подстрок, используя только что созданные значения префиксных подстрок, мы должны использовать следующую формулу:

H[I..J] * P[I]  =  H[0..J]  -  H[0..I-1]

Опять же, вы можете прочитать об этом доказательстве. в ссылке.

Итак, для решения каждого запроса я бы сделал следующее:

cin >> a >> b >> len;
if(a == b){      // just avoid extra calculation, saves little time
    cout << "Yes" << endl;
}else{
    long long h1 = h[a+len-1] % m;
    if(a){
        h1 = (m + (h1 - h[a-1]) % m) % m;
    }
    long long h2 = h[b+len-1] % m;
    if(b){
        h2 = (m + (h2 - h[b-1]) % m) % m;
    }
    if (a < b && h1 * p_pow[b-a] % m == h2 % m || a > b && h1 % m == h2 * p_pow[a-b] % m){
        cout << "Yes" << endl;
    }else{
        cout << "No" << endl;
    }
}
0 голосов
/ 16 апреля 2020

Ваш подход очень сложен и сложен для такой простой задачи. Предполагая, что вам нужно сделать эту операцию только один раз. Вы можете сравнить подстроки вручную с for l oop. Нет необходимости в хешировании. Посмотрите на этот код:

for(int i = a, j = b, counter = 0 ; counter < L ; counter++, i++, j++){
        if(S[i] != S[j]){
            cout << "Not the same" << endl;
            return 0;
        }
    }
    cout << "They are the same" << endl;
...