Какие алгоритмы работы со строками знаете?cplus-53

Работа со строками - фундаментальная задача в программировании. Рассмотрим ключевые алгоритмы и методы для эффективной обработки строк в C и C++.

1. Базовые операции со строками

Определение длины строки

В C:

size_t strlen(const char* str) {
    const char* s = str;
    while(*s) ++s;
    return s - str;
}

В C++:

std::string s = "example";
size_t len = s.length(); // или s.size()

Копирование строк

Безопасное копирование в C:

char* strcpy_safe(char* dest, const char* src, size_t dest_size) {
    size_t i = 0;
    while (i < dest_size - 1 && src[i]) {
        dest[i] = src[i];
        i++;
    }
    dest[i] = '\0';
    return dest;
}

В C++:

std::string src = "source";
std::string dest = src; // автоматическое управление памятью

2. Поиск в строках

Наивный поиск подстроки )

size_t naive_search(const std::string& text, const std::string& pattern) {
    for(size_t i = 0; i <= text.size() - pattern.size(); ++i) {
        bool found = true;
        for(size_t j = 0; j < pattern.size(); ++j) {
            if(text[i+j] != pattern[j]) {
                found = false;
                break;
            }
        }
        if(found) return i;
    }
    return std::string::npos;
}

Алгоритм Кнута-Морриса-Пратта )

std::vector<int> computeLPS(const std::string& pattern) {
    std::vector<int> lps(pattern.size());
    int len = 0;
    lps[0] = 0;
    
    for(int i = 1; i < pattern.size(); ) {
        if(pattern[i] == pattern[len]) {
            lps[i++] = ++len;
        } else {
            if(len != 0) len = lps[len-1];
            else lps[i++] = 0;
        }
    }
    return lps;
}

size_t KMP_search(const std::string& text, const std::string& pattern) {
    auto lps = computeLPS(pattern);
    int i = 0, j = 0;
    while(i < text.size()) {
        if(text[i] == pattern[j]) {
            i++; j++;
            if(j == pattern.size()) return i - j;
        } else {
            if(j != 0) j = lps[j-1];
            else i++;
        }
    }
    return std::string::npos;
}

3. Сравнение строк

Лексикографическое сравнение:

int str_compare(const char* a, const char* b) {
    while(*a && *b && *a == *b) { a++; b++; }
    return *a - *b;
}

В C++:

std::string s1 = "apple", s2 = "banana";
if(s1 < s2) { /* ... */ } // перегруженные операторы

4. Конкатенация строк

В C:

char* str_concat(char* dest, const char* src, size_t dest_size) {
    size_t dest_len = strlen(dest);
    size_t i = 0;
    while(dest_len + i < dest_size - 1 && src[i]) {
        dest[dest_len + i] = src[i];
        i++;
    }
    dest[dest_len + i] = '\0';
    return dest;
}

В C++:

std::string s1 = "Hello", s2 = "World";
std::string result = s1 + " " + s2;

5. Разделение строк

В C (strtok):

char str[] = "one,two,three";
char* token = strtok(str, ",");
while(token != NULL) {
    printf("%s\n", token);
    token = strtok(NULL, ",");
}

В C++ (stringstream):

std::string s = "one two three";
std::istringstream iss(s);
std::string token;
while(iss >> token) {
    std::cout << token << std::endl;
}

6. Алгоритмы преобразования строк

Преобразование регистра

void to_upper(std::string& s) {
    for(auto& c : s) c = toupper(c);
}

Удаление пробелов

std::string remove_whitespace(const std::string& s) {
    std::string result;
    std::copy_if(s.begin(), s.end(), std::back_inserter(result),
                [](char c){ return !std::isspace(c); });
    return result;
}

7. Хеширование строк

Полиномиальное хеширование:

size_t string_hash(const std::string& s, size_t p = 31, size_t m = 1e9+9) {
    size_t hash = 0;
    size_t p_pow = 1;
    for(char c : s) {
        hash = (hash + (c - 'a' + 1) * p_pow) % m;
        p_pow = (p_pow * p) % m;
    }
    return hash;
}

8. Регулярные выражения

#include <regex>
std::string s = "Email: test@example.com";
std::regex email_regex(R"(\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b)");
std::smatch matches;
if(std::regex_search(s, matches, email_regex)) {
    std::cout << "Found email: " << matches[0] << std::endl;
}

Оптимизации и советы

  1. Избегайте лишних копий - используйте string_view (C++17)
  2. Для частых конкатенаций - используйте std::ostringstream
  3. Для критичного к производительности кода - рассмотрите алгоритмы Бойера-Мура или Рабина-Карпа
  4. Для Unicode строк - используйте специализированные библиотеки (ICU)

Резюмируем: эффективная работа со строками требует понимания как базовых алгоритмов, так и современных методов. В C++ предпочтительно использовать std::string и алгоритмы STL, в C - аккуратно работать с ручным управлением памятью. Выбор алгоритма зависит от конкретной задачи и требований к производительности.