Самая длинная общая подстрока с использованием рекурсии и DP

Я пытаюсь найти самую длинную общую подстроку из двух строк, используя рекурсию и DP. Обратите внимание, что я не имею в виду самую длинную смежную подпоследовательность. Итак, если бы две струны были

String s1 = "abcdf"; String s2 = "bzcdf" 
Longest Common Substring == "cdf" (not "bcdf").
Basically they have to be continuous elements

Я пытаюсь сделать это с помощью рекурсии и возврата. Однако проблема в том, что если я использую рекурсию, такую ​​как ниже, +1 добавляются заранее во фрейм, который находится выше в стеке вызовов, и не знает, являются ли следующие символы действительно сплошные элементы или нет. Итак, следуя приведенному выше примеру, ответом будет "bcdf".

public class ThisIsLongestCommonSubsequence_NotSubstring {
public static void main(String[] args) {

    String s1 = "abcdgh";
    String s2 = "abefgh";
    System.out.println(fun(s1, s1.length()-1, s2, s2.length()-1));
}

static int fun(String s1, int i, String s2, int j)
{
    if(i == -1 || j == -1)
        return 0;

    int ret = 0;
    if(s1.charAt(i) == s2.charAt(j))
        ret = fun(s1, i-1, s2, j-1) + 1;
    else
        ret = max(fun(s1, i-1, s2, j), fun(s1, i, s2, j-1));

    return ret;
}

static int max(int a, int b)
{
    return a>b?a:b;
}
}

На данный момент я придумал приведенный ниже код. Обратите внимание, как я сбрасываю счетчик на 0 каждый раз, когда нахожу несоответствие. И отслеживайте количество совпадающих символов с помощью переменной с именем int count и записывайте наибольшее значение в любой момент программы, используя переменную с именем int maxcount. Мой код ниже.

public class LongestContinuousSubstringGlobalvariable {

static int maxcount = 0;

public static void main(String[] args) {
    String s1 = "abcdghijl";
    String s2 = "abefghijk";

    fun(s1, s2, s1.length()-1, s2.length()-1, 0);
    System.out.println("maxcount == "+maxcount);
}

static void fun(String s1, String s2, int i, int j, int count)
{
    if(i == -1 || j==-1)
        return;

    if(s1.charAt(i) == s2.charAt(j))
    {
        if(count+1 >  maxcount)
            maxcount = count+1;
        fun(s1, s2, i-1, j-1, count+1); 
    }
    else
    {
        fun(s1, s2, i-1, j, 0);
        fun(s1, s2, i, j-1, 0);
    }
}
}

Это прекрасно работает. Однако есть несколько вещей, которые мне не нравятся в моем коде.

  1. Использование глобальной переменной (static int maxcount) для сравнения по кадрам
  2. Я не думаю, что это настоящее динамическое программирование или возврат с возвратом, поскольку нижний кадр не возвращает его вывод в более высокий кадр, который затем решает, что с ним делать.

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

PS: Мне известны другие подходы к проблеме, например, сохранение матрицы и выполнение чего-то вроде

M [i] [j] = M [i-1] [j-1] +1, если (str [i] == str [j])

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


person saltandwater    schedule 08.07.2016    source источник
comment
Возможный дубликат самой длинной общей подстроки   -  person Prune    schedule 08.07.2016


Ответы (1)


Вероятно, это можно было бы сделать на Прологе. Ниже приведен код, который я мог бы записать с помощью этого сообщения: Foreach не работает в Prolog, http://obvcode.blogspot.in/2008/11/working-with-strings-in-prolog.html и Как найти самый длинный список в списке списков?

myrun(S1, S2):-
    writeln("-------- codes of first string ---------"),
    string_codes(S1, C1list),
    writeln(C1list),

    writeln("-------- codes of second string ---------"),
    string_codes(S2, C2list),
    writeln(C2list),

    writeln("--------- substrings of first --------"),
    findall(X, sublist(X, C1list), L),   
    writeln(L),

    writeln("--------- substrings of second --------"),
    findall(X, sublist(X, C2list), M),
    writeln(M),

    writeln("------ codes of common substrings -------"),
    intersection(L,M, Outl),
    writeln(Outl), 

    writeln("--------- common strings in one line -------"),
    maplist(string_codes, Sl, Outl), 
    writeln(Sl),
    writeln("------ common strings one by one -------"),
    maplist(writeln, Sl),

    writeln("------ find longest -------"),
    longest(Outl, LongestL),
    writeln(LongestL),
    string_codes(LongestS, LongestL),
    writeln(LongestS).

sublist(S, L) :-
  append(_, L2, L),
  append(S, _, L2).

longest([L], L) :-
   !.
longest([H|T], H) :- 
   length(H, N),
   longest(T, X),
   length(X, M),
   N > M,
   !.
longest([H|T], X) :-
   longest(T, X),
   !.

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

?- myrun("abcdf", "bzcdf").
-------- codes of first string ---------
[97,98,99,100,102]
-------- codes of second string ---------
[98,122,99,100,102]
--------- substrings of first --------
[[],[97],[97,98],[97,98,99],[97,98,99,100],[97,98,99,100,102],[],[98],[98,99],[98,99,100],[98,99,100,102],[],[99],[99,100],[99,100,102],[],[100],[100,102],[],[102],[]]
--------- substrings of second --------
[[],[98],[98,122],[98,122,99],[98,122,99,100],[98,122,99,100,102],[],[122],[122,99],[122,99,100],[122,99,100,102],[],[99],[99,100],[99,100,102],[],[100],[100,102],[],[102],[]]
------ codes of common substrings -------
[[],[],[98],[],[99],[99,100],[99,100,102],[],[100],[100,102],[],[102],[]]
--------- common strings in one line -------
[,,b,,c,cd,cdf,,d,df,,f,]
------ common strings one by one -------


b

c
cd
cdf

d
df

f

------ find longest -------
[99,100,102]
cdf
true.

Не обращайте внимания на «истину» в конце.

Если убрать пояснительные части, программа будет намного короче:

myrun(S1, S2):-
    string_codes(S1, C1list),
    string_codes(S2, C2list),
    findall(X, sublist(X, C1list), L),    
    findall(X, sublist(X, C2list), M),
    intersection(L,M, Outl),
    longest(Outl, LongestL),
    string_codes(LongestS, LongestL),
    writeln(LongestS).

sublist(S, L) :-
  append(_, L2, L),
  append(S, _, L2).

longest([L], L) :-
   !.
longest([H|T], H) :- 
   length(H, N),
   longest(T, X),
   length(X, M),
   N > M,
   !.
longest([H|T], X) :-
   longest(T, X),
   !.


?- myrun("abcdf", "bzcdf").
cdf
true.
person rnso    schedule 08.07.2016