выяснить, 2 строки похожи или нет - PullRequest
3 голосов
/ 11 сентября 2011

Правила: 2 строки, a и b, обе они состоят из символов ASCII и символов, не относящихся к ASCII (скажем, китайские иероглифы, закодированные в gbk).

If the non-ASCII chars contained in b also show up in a and no less than the times they appear in b, then we say b is similar with a.

Например:

a = "ab中ef日jkl中本"  //non-ASCII chars:'中'(twice), '日'(once), '本'(once)
b = "bej中中日"  //non-ASCII chars:'中'(twice), '日'(once)
c = 'lk日日日'   //non-ASCII chars:'日'(3 times, more than twice in a)

согласно правилу, b похож на a, а c - нет. Вот мой вопрос: Мы не знаем, сколько символов не ASCII существует в a и b, вероятно, много. Итак, чтобы узнать, сколько раз в символах a и b появляются символы, не относящиеся к ASCII, я должен использовать Hash-Table для хранения их времени появления? Возьмем строку а в качестве примера:

[non-ASCII's hash-value]:[times]
     中's hash-val      : 2
     日's hash-val      : 1
     本's hash-val      : 1

Проверьте строку b, если мы встретим не-ASCII-символ в b, то хешируем его и проверяем хеш-таблицу a, если char присутствует в хеш-таблице a, то время его появления уменьшается на 1. Если время появления меньше 0 (-1), то мы говорим, что b не похоже на a.

Или есть способ получше?

PS: Я читаю строку побайтно, если байт меньше 128, то я беру это как символ ASCII, в противном случае я принимаю его как часть не-ASCII символа (многобайтовый). Это то, что я делаю, чтобы узнать не-ASCII-символы. Это правильно?

1 Ответ

7 голосов
/ 11 сентября 2011

Вы задали два вопроса:

  1. Можем ли мы считать не-ASCII-символы, используя хеш-таблицу? Ответ: конечно. Когда вы читаете символы (, а не байты ), изучите кодовые точки. Для любой кодовой точки, превышающей 127, поместите ее в хеш-таблицу подсчета. Для символа c добавьте (c, 1), если c нет в таблице, и обновите (c, x) до (c, x + 1), если c уже есть в таблице.

  2. Есть ли лучший способ решить эту проблему, чем ваш подход увеличения счетчиков в a и уменьшения при прохождении через b? Если ваша хеш-таблица дает почти O (1) доступ, то я подозреваю, что нет. Вы смотрите на каждый символ в строке ровно один раз, и для каждого символа вы выполняете либо вставку хеш-таблицы, либо поиск, а также сложение или вычитание и проверку на 0. С несортированными строками у вас есть для в любом случае, посмотрите на все символы в обеих строках, так что вы, я думаю, нашли лучшее решение.

Интервьюер может искать, чтобы вы сказали что-то вроде: «Хммммм, если бы эти строки были действительно массивными файлами, которые не могли поместиться в памяти, что бы я делал?» Или для вас, чтобы спросить: «Ну, строка отсортирована? Потому что, если они, я могу сделать это быстрее ...».

Но теперь давайте скажем, что строки огромные. Единственное, что вы храните в памяти - это хеш-таблица. Unicode имеет только около 1 миллиона кодовых точек, и вы сохраняете целое число для каждой, так что даже если вы получаете данные из файлов размером в гигабайт, вам понадобится всего около 4 МБ или около того для вашей хеш-таблицы (или небольшого кратного этого, так как будет быть над головой).

При отсутствии каких-либо других условий ваш алгоритм хорош. Сортировка строк заранее не годится; он занимает больше памяти и не является линейной операцией.

ДОПОЛНЕНИЕ

Поскольку в ваших первоначальных комментариях упоминался тип char, а не wchar_t, я подумал, что приведу пример использования широких строк. См http://codepad.org/B3MXOgqc

Надеюсь, это поможет.

ADDENDUM 2

Хорошо, вот программа на C, которая показывает, как пройти через широкую строку и работать на уровне персонажа:

http://codepad.org/QVX3QPat

Это очень короткая программа, поэтому я также вставлю ее сюда:

#include <stdio.h>
#include <string.h>
#include <wchar.h>

char *s1 = "abd中日";
wchar_t *s2 = L"abd中日";

int main() {
    int i, n;
    printf("length of s1 is %d\n", strlen(s1));
    printf("length of s2 using wcslen is %d\n", wcslen(s2));
    printf("The codepoints of the characters of s2 are\n");
    for (i = 0, n = wcslen(s2); i < n; i++) {
        printf("%02x\n", s2[i]);
    } 
    return 0;
}

Выход:

length of s1 is 9
length of s2 using wcslen is 5
The codepoints of the characters of s2 are
61
62 
64
4e2d
65e5

Чему мы можем научиться из этого? Пара вещей:

  1. Если вы используете простые старые char для символов CJK, тогда длина строки будет неправильной .
  2. Чтобы использовать символы Юникода в C, используйте wchar_t
  3. Строковые литералы имеют ведущий L для широких строк

В этом примере я определил строку с символами CJK и использовал wchar_t и цикл for с wcslen. Обратите внимание, что я работаю с реальными символами, а не с байтами, поэтому я получаю правильное количество символов, равное 5. Теперь я распечатываю каждую кодовую точку. В своем вопросе к собеседованию вы узнаете, является ли кодовая точка >= 128. Я показал их в Hex, как и в культуре, поэтому вы можете найти > 0x7F. : -)

ADDENDUM 3

Несколько заметок в http://tldp.org/HOWTO/Unicode-HOWTO-6.html стоит прочитать. Обработка символов намного больше, чем показывает простой пример, приведенный выше. В комментариях ниже Я. Ф. Себастьян приводит ряд других важных ссылок.

Из немногих вещей, которые необходимо решить, это нормализация. Например, заботится ли ваш интервьюер о том, что, если ему даны две строки, одна из которых содержит только other, а другая C, за которой следует КОМБИНИРОВАННАЯ МАРКА CEDILLA НИЖЕ, они будут одинаковыми? Они представляют один и тот же символ , но один использует одну кодовую точку, а другой - две.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...