Nano Hash - криптовалюты, майнинг, программирование

Как найти самую длинную общую подстроку с помощью C++

Я искал в Интернете реализацию C++ Longest Common Substring, но не смог найти достойную. Мне нужен алгоритм LCS, который возвращает саму подстроку, так что это не просто LCS.

Однако мне было интересно, как я могу сделать это между несколькими строками.

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

Любая идея о том, как это можно ускорить для нескольких строк? Спасибо.

Важное редактирование Одна из переменных, которые мне даны, определяет количество строк, в которых должна быть самая длинная общая подстрока, поэтому мне можно дать 10 строк и найти LCS для них всех (K=10 ), или LCS из 4 из них, но мне не говорят, какие 4, я должен найти лучшие 4.

20.04.2012

  • Если вам нужно сделать это с несколькими строками, вам не следует следовать вашему подходу. Учтите, что LCS в целом может не быть подмножеством LCS между двумя конкретными строками [ej. 123asdfg, asdfg123, 123; если вы запустите LCS на первых двух, вы получите asdfg, который не имеет общих символов с последней строкой]. Что касается возврата фактической подстроки LCS, общий алгоритм заканчивается таблицей, по которой вы можете пройти, чтобы создать такую ​​​​строку за линейное время (по размеру LCS). 20.04.2012
  • markusstengel.de/text/en/i_4_1_5_3.html 20.04.2012
  • Проверьте здесь Анализ наиболее длинных общих совпадений подстрок 21.10.2014

Ответы:


1

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

20.04.2012
  • Очень простая статья о массивах суффиксов, подходящая для этой задачи. Быстро разбудил меня. Очень хорошо. 26.03.2015

  • 2

    Ответ: ОБОБЩЕННОЕ СУФФИКСНОЕ ДЕРЕВО. http://en.wikipedia.org/wiki/Generalized_suffix_tree

    Вы можете построить обобщенное суффиксное дерево с несколькими строками.

    Посмотрите на эту http://en.wikipedia.org/wiki/Longest_common_substring_problem.

    Суффиксное дерево может быть построено за время O(n) для каждой строки, всего k*O(n). K - общее количество строк.

    Так что очень быстро решить эту проблему.

    20.04.2012
  • Спасибо! Любая ссылка на суффиксные деревья и С++? Я никогда не использовал их, поэтому мне нужно научиться. 20.04.2012

  • 3

    Это задача динамического программирования, и ее можно решить за время O(mn), где m — длина одной строки, а n — другой.

    Как и любую другую задачу, решаемую с помощью динамического программирования, мы разделим задачу на подзадачи. Допустим, если две строки равны x1x2x3....xm и y1y2y3...yn

    S(i,j) — самая длинная общая строка для x1x2x3...xi и y1y2y3....yj, тогда

    S(i,j) = max { длина самой длинной общей подстроки, оканчивающейся на xi/yj, если ( x[i] == y[j] ), S(i-1, j-1), S(i, j -1), S(i-1, j) }

    Вот рабочая программа на Java. Я уверен, что вы можете преобразовать его в С++.:

    public class LongestCommonSubstring {
    
        public static void main(String[] args) {
            String str1 = "abcdefgijkl";
            String str2 = "mnopabgijkw";
            System.out.println(getLongestCommonSubstring(str1,str2));
        }
    
        public static String getLongestCommonSubstring(String str1, String str2) {
            //Note this longest[][] is a standard auxialry memory space used in Dynamic
                    //programming approach to save results of subproblems. 
                    //These results are then used to calculate the results for bigger problems
            int[][] longest = new int[str2.length() + 1][str1.length() + 1];
            int min_index = 0, max_index = 0;
    
                    //When one string is of zero length, then longest common substring length is 0
            for(int idx = 0; idx < str1.length() + 1; idx++) {
                longest[0][idx] = 0;
            }
    
            for(int idx = 0; idx < str2.length() + 1; idx++) {
                longest[idx][0] = 0;
            }
    
            for(int i = 0; i <  str2.length(); i++) {
                for(int j = 0; j < str1.length(); j++) {
    
                    int tmp_min = j, tmp_max = j, tmp_offset = 0;
    
                    if(str2.charAt(i) == str1.charAt(j)) {
                        //Find length of longest common substring ending at i/j
                        while(tmp_offset <= i && tmp_offset <= j &&
                                str2.charAt(i - tmp_offset) == str1.charAt(j - tmp_offset)) {
    
                            tmp_min--;
                            tmp_offset++;
    
                        }
                    }
                    //tmp_min will at this moment contain either < i,j value or the index that does not match
                    //So increment it to the index that matches.
                    tmp_min++;
    
                    //Length of longest common substring ending at i/j
                    int length = tmp_max - tmp_min + 1;
                    //Find the longest between S(i-1,j), S(i-1,j-1), S(i, j-1)
                    int tmp_max_length = Math.max(longest[i][j], Math.max(longest[i+1][j], longest[i][j+1]));
    
                    if(length > tmp_max_length) {
                        min_index = tmp_min;
                        max_index = tmp_max;
                        longest[i+1][j+1] = length;
                    } else {
                        longest[i+1][j+1] = tmp_max_length;
                    }
    
    
                }
            }
    
            return str1.substring(min_index, max_index >= str1.length() - 1 ? str1.length() - 1 : max_index + 1);
        }
    }
    
    25.10.2013
  • Похоже на Java, а этот вопрос помечен C++. 25.10.2013
  • Должно быть довольно легко перенести его на язык C++, не используется ничего, что было бы специфично для Java и не было бы в C++. 25.10.2013
  • Снеги, я нашел это полезным, большое спасибо. ЮХао, не хочешь ли ты сам портировать его на C++? 06.02.2014
  • Я думаю, вы закончили тем, что описали решение проблемы самой длинной общей подпоследовательности. 31.05.2015

  • 4

    Для этого есть очень элегантное решение динамического программирования.

    Пусть LCSuff[i][j] будет самым длинным общим суффиксом между X[1..m] и Y[1..n]. У нас тут два случая:

    • X[i] == Y[j], это означает, что мы можем расширить самый длинный общий суффикс между X[i-1] и Y[j-1]. Таким образом, LCSuff[i][j] = LCSuff[i-1][j-1] + 1 в этом случае.

    • X[i] != Y[j], так как сами последние символы разные, X[1..i] и Y[1..j] не могут иметь общего суффикса. Следовательно, LCSuff[i][j] = 0 в данном случае.

    Теперь нам нужно проверить максимальное из этих самых длинных общих суффиксов.

    Итак, LCSubstr(X,Y) = max(LCSuff(i,j)), где 1<=i<=m и 1<=j<=n

    Алгоритм в значительной степени пишет сам себя сейчас.

    string LCSubstr(string x, string y){
        int m = x.length(), n=y.length();
    
        int LCSuff[m][n];
    
        for(int j=0; j<=n; j++)
            LCSuff[0][j] = 0;
        for(int i=0; i<=m; i++)
            LCSuff[i][0] = 0;
    
        for(int i=1; i<=m; i++){
            for(int j=1; j<=n; j++){
                if(x[i-1] == y[j-1])
                    LCSuff[i][j] = LCSuff[i-1][j-1] + 1;
                else
                    LCSuff[i][j] = 0;
            }
        }
    
        string longest = "";
        for(int i=1; i<=m; i++){
            for(int j=1; j<=n; j++){
                if(LCSuff[i][j] > longest.length())
                    longest = x.substr((i-LCSuff[i][j]+1) -1, LCSuff[i][j]);
            }
        }
        return longest;
    }
    
    31.05.2015
  • Можете ли вы объяснить последнюю часть, где вы узнаете общую подстроку? 26.03.2016
  • @SexyBeast Мы знаем, что LCS[i][j] дает длину самого длинного общего суффикса, заканчивающегося индексом i-1 для строки x и заканчивающимся индексом j-1 для строки y. Таким образом, поиск общего суффикса — это всего лишь вопрос получения суффикса длины LCS[i][j] из любой из строк. В приведенном выше ответе для этого используется первая строка x. 29.03.2017
  • В приведенном выше коде есть ошибка, но редактирование невозможно (из-за правил минимально возможного размера редактирования). LCSuff должен иметь размер (m+1, n+1), а не (m, n). Нижнюю часть также можно легко улучшить, отслеживая максимальную текущую длину подстроки и начало/конец подстроки в одной из строк. Строка может быть извлечена, например. x.substr (start_substr, len_substr). 17.04.2018

  • 5

    Вот версия C# для поиска самой длинной общей подстроки с использованием динамического программирования двух массивов (вы можете обратиться к: http://codingworkout.blogspot.com/2014/07/longest-common-substring.html для более подробной информации)

    class LCSubstring
            {
                public int Length = 0;
                public List<Tuple<int, int>> indices = new List<Tuple<int, int>>();
            }
            public string[] LongestCommonSubStrings(string A, string B)
            {
                int[][] DP_LCSuffix_Cache = new int[A.Length+1][];
                for (int i = 0; i <= A.Length; i++)
                {
                    DP_LCSuffix_Cache[i] = new int[B.Length + 1];
                }
                LCSubstring lcsSubstring = new LCSubstring();
                for (int i = 1; i <= A.Length; i++)
                {
                    for (int j = 1; j <= B.Length; j++)
                    {
                        //LCSuffix(Xi, Yj) = 0 if X[i] != X[j]
                        //                 = LCSuffix(Xi-1, Yj-1) + 1 if Xi = Yj
                        if (A[i - 1] == B[j - 1])
                        {
                            int lcSuffix = 1 + DP_LCSuffix_Cache[i - 1][j - 1];
                            DP_LCSuffix_Cache[i][j] = lcSuffix;
                            if (lcSuffix > lcsSubstring.Length)
                            {
                                lcsSubstring.Length = lcSuffix;
                                lcsSubstring.indices.Clear();
                                var t = new Tuple<int, int>(i, j);
                                lcsSubstring.indices.Add(t);
                            }
                            else if(lcSuffix == lcsSubstring.Length)
                            {
                                //may be more than one longest common substring
                                lcsSubstring.indices.Add(new Tuple<int, int>(i, j));
                            }
                        }
                        else
                        {
                            DP_LCSuffix_Cache[i][j] = 0;
                        }
                    }
                }
                if(lcsSubstring.Length > 0)
                {
                    List<string> substrings = new List<string>();
                    foreach(Tuple<int, int> indices in lcsSubstring.indices)
                    {
                        string s = string.Empty;
                        int i = indices.Item1 - lcsSubstring.Length;
                        int j = indices.Item2 - lcsSubstring.Length;
                        Assert.IsTrue(DP_LCSuffix_Cache[i][j] == 0);
                        for(int l =0; l<lcsSubstring.Length;l++)
                        {
                            s += A[i];
                            Assert.IsTrue(A[i] == B[j]);
                            i++;
                            j++;
                        }
                        Assert.IsTrue(i == indices.Item1);
                        Assert.IsTrue(j == indices.Item2);
                        Assert.IsTrue(DP_LCSuffix_Cache[i][j] == lcsSubstring.Length);
                        substrings.Add(s);
                    }
                    return substrings.ToArray();
                }
                return new string[0];
            }
    

    Где модульные тесты:

    [TestMethod]
            public void LCSubstringTests()
            {
                string A = "ABABC", B = "BABCA";
                string[] substrings = this.LongestCommonSubStrings(A, B);
                Assert.IsTrue(substrings.Length == 1);
                Assert.IsTrue(substrings[0] == "BABC");
                A = "ABCXYZ"; B = "XYZABC";
                substrings = this.LongestCommonSubStrings(A, B);
                Assert.IsTrue(substrings.Length == 2);
                Assert.IsTrue(substrings.Any(s => s == "ABC"));
                Assert.IsTrue(substrings.Any(s => s == "XYZ"));
                A = "ABC"; B = "UVWXYZ";
                string substring = "";
                for(int i =1;i<=10;i++)
                {
                    A += i;
                    B += i;
                    substring += i;
                    substrings = this.LongestCommonSubStrings(A, B);
                    Assert.IsTrue(substrings.Length == 1);
                    Assert.IsTrue(substrings[0] == substring);
                }
            }
    
    31.07.2014

    6

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

    #include <iostream>
    
    std::string lcs( std::string a, std::string b )
    {
        if( a.empty() || b.empty() ) return {} ;
    
        std::string current_lcs = "";
    
        for(int i=0; i< a.length(); i++) {
            size_t fpos = b.find(a[i], 0);
            while(fpos != std::string::npos) {
                std::string tmp_lcs = "";
                tmp_lcs += a[i];
                for (int x = fpos+1; x < b.length(); x++) {
                    tmp_lcs+=b[x];
                    size_t spos = a.find(tmp_lcs, 0);
                    if (spos == std::string::npos) {
                        break;
                    } else {
                        if (tmp_lcs.length() > current_lcs.length()) {
                            current_lcs = tmp_lcs;
                        }
                    }
                }
                fpos = b.find(a[i], fpos+1);
            }
        }
        return current_lcs;
    }
    
    int main(int argc, char** argv)
    {
        std::cout << lcs(std::string(argv[1]), std::string(argv[2])) << std::endl;
    }
    
    07.04.2018

    7

    Найдите наибольшую подстроку из всех рассматриваемых строк. Из N строк у вас будет N подстрок. Выберите самый большой из этих N.

    20.04.2012
  • Не могли бы вы пояснить свой ответ, я действительно не могу его понять, каков ваш результат: abdac, aabbdc, aac? 20.04.2012
  • Новые материалы

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

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..