最长公共子串的方法

时间:2013-12-20 16:50:04

标签: c++ string algorithm lcs longest-substring

Common Child的编程挑战中,我采用了与一般最长公共子串问题不同的方法。守则

#include <cmath>
#include <cstdio>
#include <vector>
#include<string>
#include <iostream>
#include <algorithm>
using namespace std;

void max(string a,string b,int n)
{
    int count=0,x=-1,prev=0,i,j,k;
    for(i=0;i<n;i++)
    {
        x=-1;
        for(j=i;j<n;j++)
        {
            for(k=x+1;k<n;k++)
            {
                if(a[j]==b[k])
                {
                    count++;
                    x=k;
                    break;
                }
            }
        }
        if(prev<count)
        {
            prev=count;
        }
        count=0;        
    }
    cout<<prev;
}

int main() {
    string a,b;
    int n;
    cin>>a;
    cin>>b;
    n=a.length();
    max(a,b,n);
    return 0;

采用较小的TestCases我能够获得解决方案,但我无法获得更长的解决方案。

我做的是

  

输入:ABCDEF - 让它成为FBDAMN - 让它成为b,因为它被插入   代码。输出:2。即。因为BD是子串。

     

所以我在这里做的是检查每个的匹配子字符串   a的子串,并打印最大的。即。 ABCDEF的子串,   BCDEF,CDEF,DEF,EF,F在这里表示最外层的循环。(循环i)

     

现在第二个循环匹配字符串中的每个字母即。它迭代   for(1 ... n),(2 ... n),(3 ... n),...,(n),意思是从字母开始   我指定。

     

现在第三个循环遍历整个字符串B以查看是否   a [j]与B中的任何字母匹配,如果是,则计算增量。

     

因为我们只能从子串中删除字母而不是混淆它,   即每个字母在两个字母中应该具有相同的相对顺序   字符串,我在找到上一封信之后搜索B,使用   X

     

干运行就像示例(从0到n-1的数组)

     

a = abcdef b = fbdamn

     

当i = 0时 - 整个字符串匹配abcdef

     

x = -1所以k从0迭代到n-1所以,a = f? A = B? A = d? A = A? count =   计数+ 1;所以x设置为3(a的位置)。 A之后的元素可以   只发生在B中,所以x = 3。因此k迭代从4到5 b = m?   B = N + C = M + C = N? ...... ......

     

现在我们保存先前计数的值,如果它大于计数   之前。所以maxcount = count然后重置count到0,然后重置   位置x = -1。

     

i = 1,即string = BCDEF k从0到n所以,B = F? B =? count = count + 1 //   将1 x设为1(B的位置)k从2到n c = d? C = A C = M + C = N?   k从2到n d = d? count = count + 1 //变为2 x从3设置为2 k   到n e = a?e = m?e = n? k从3到n f = a?f = m?f = n?然后如果最大计数(prev in   代码)大于当前计数,然后maxcount = count,reset count =   0,x = -1;那么对于CDEF,DEF,EF,F就是这样的最大子串   这里变成了2

     

适用于较短的测试用例,而不适用于较长的测试用例。

它适用的测试用例是:

OUDFRMYMAW AWHYFCCMQX

使用输出2

不起作用

WEWOUCUIDGCGTRMEZEPXZFEJWISRSBBSYXAYDFEJJDLEBVHHKS FDAGCXGKCTKWNECHMRXZWMLRYUCOCZHJRRJBOAJOQJZZVUYXIC

输出正确 15 ,输出 10 。任何人都可以向我解释我在

中犯了什么错误

3 个答案:

答案 0 :(得分:6)

我预见的一个问题如下:

如果a = ABCDEFG且b = ACDEFGB,

现在它首先匹配A,然后它将匹配B,但是k已经变为6。因此,CDEFG的其他字母将不会匹配。

因此,应跳过可能的字符串ACDEFG。

您的代码应将最大的普通孩子作为CDEFG返回。

编辑:这不是最常见的子串问题,而是最常见的子序列问题。 http://www.geeksforgeeks.org/dynamic-programming-set-4-longest-common-subsequence/

答案 1 :(得分:4)

问题在于你的算法使用了一种天真的贪心方法:它在看到它时立即进行匹配,并且在此之后再也不会重新考虑它的决定,直到它到达下一个子串。可以通过反例证明这种贪婪策略不适用于LCS:考虑字符串

A = "abcd"
B = "acdb"

您的算法returns 2,因为它为"ab"提供资金,而最长的子序列为"acd",长度为3。

这两个字符串经过精心构造,可以欺骗您的算法产生错误的结果。您的算法将匹配初始位置的'a' s,前进到字符串'b'的第二个字符A,并在字符串B的最后位置匹配它。此时,您的算法注定失败:它找不到'c''d',因为B的所有字符都已耗尽。它应该做的就是通过回溯匹配'b'的决定,好像它可以做得更好。这个问题的答案是响亮的“是”:如果我们跳过A的第二个字符,我们会匹配'c''d',以获得正确的结果。

LCS的正确实现(使用DP或带有memoization)可以实现这种回溯,而不会以指数方式增加时间。它是研究和实现的最简单的DP算法之一,因此您可以毫无困难地应用它来解决手头的问题。

答案 2 :(得分:0)

import java.util.Scanner;
  public class JavaApplication8 {
      public static int find(String s1,String s2){
        int n = s1.length();
        int m = s2.length();
        int ans = 0;
        int[] a = new int[m];
        int b[] = new int[m];
        for(int i = 0;i<n;i++){
            for(int j = 0;j<m;j++){
                if(s1.charAt(i)==s2.charAt(j)){
                   if(i==0 || j==0 )a[j] = 1;
                   else{
                       a[j] = b[j-1] + 1;
                   }
                   ans = Math.max(ans, a[j]);
                }

            }
            int[] c = a;
            a = b;
            b = c;
        }
        return ans;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s1 = sc.next();
        String s2 = sc.next();
        System.out.println(find(s1,s2));
    }

}

Time Complexity O(N). Space Complexity O(N).