【问题标题】:Approach to Longest Common Substring最长公共子串的逼近
【发布时间】:2013-12-20 16:50:04
【问题描述】:

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;

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

我做的是

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

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

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

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

由于我们只能从子字符串中删除字母而不能将其弄乱, 即每个字母在两者中都应具有相同的相对顺序 字符串,我在找到上一个字母后搜索 B,使用 x.

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

a= abcdef b= fbdamn

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

x=-1 所以 k 从 0 迭代到 n-1 所以,a=f? a=b? a=d?一个=一个?计数 = 计数+1;所以x设置为3(a的位置)。 A中a之后的元素可以 仅出现在 B 中的 a 之后,因此 x=3。因此 k 从 4 迭代到 5 b=m? b=n? c=m?c=n? .... ... ...

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

i=1 即字符串 = BCDEF k 从 0 到 n 所以,B=F? b=b?计数=计数+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 设置为 2 k 从 3 到 n e=a?e=m?e=n? k 从 3 到 n f=a?f=m?f=n?那么如果最大计数(prev in 代码)大于当前计数,则 maxcount = count,重置 count = 0,x=-1;然后对于 CDEF,DEF,EF,F 的最大子字符串是这样的 这里变成了 2

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

它适用的测试用例是:

OUDFRMYMAW AWHYFCCMQX

带输出 2

不适合

WEWOUCUIDGCGTRMEZEPXZFEJWISRSBBSYXAYDFEJJDLEBVHHKS FDAGCXGKCTKWNECHMRXZWMLRYUCOCZHJRRJBOAJOQJZZVUYXIC

正确的输出是 15 而我的输出是 10。任何人都可以向我解释我在哪里犯了错误

【问题讨论】:

  • 我不明白这个问题的最后投票:问题是完整的,并且非常有意义。

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


【解决方案1】:

我预见的一个问题如下:

如果 a = ABCDEFG 且 b = ACDEFGB,

现在它会先匹配 A,然后匹配 B,然后 k 已经变成了 6。因此,不会匹配更多的字母 CDEFG。

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

您的代码应以 CDEFG 形式返回最大的共同子节点。

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

【讨论】:

    【解决方案2】:

    问题在于您的算法使用了一种幼稚的贪婪方法:它一看到它就进行匹配,之后不再重新考虑它的决定,直到它到达下一个子字符串。可以用一个反例来证明这种贪婪策略不适用于LCS:考虑字符串

    A = "abcd"
    B = "acdb"
    

    你的算法returns 2,因为它资助了"ab",而最长的子序列是"acd",长度为3。

    这两个字符串是经过精心构造的,目的是诱使您的算法产生不正确的结果。您的算法将在初始位置匹配'a's,前进到字符串A 的第二个字符'b',并在字符串B 的最后一个位置匹配它。此时,你的算法注定失败:它找不到'c''d',因为B 的所有字符都用完了。它应该做的是,通过回溯匹配'b' 的决定,它似乎可以做得更好。这个答案是响亮的“是”:如果我们跳过A 的第二个字符,我们将匹配'c''d',得到正确的结果3。

    LCS 的正确实现(使用 DP 或使用 memoization)可以在不成倍增加时间的情况下进行这种回溯。它是学习和实现的最简单的 DP 算法之一,因此您应该可以毫不费力地应用它来解决手头的问题。

    【讨论】:

      【解决方案3】:
      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).

      【讨论】:

        猜你喜欢
        • 2014-04-14
        • 2016-06-03
        • 2010-11-28
        • 2014-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-01
        • 1970-01-01
        相关资源
        最近更新 更多