鉴于两个字符串,找到最长的常见字符包

时间:2010-08-21 05:08:57

标签: string algorithm

这个问题与从两个字符串中找到最长序列或子字符串的方式略有不同。

给定两个大小相同的字符串N,找到每个字符串中最长的子字符串,使得子字符串包含相同的字符包。

两个子串可能不一定具有相同的序列。但他们必须拥有相同的字符包。

例如,

a = ABCDDEGF b = FPCDBDAX

最长匹配的字符包是ABCDD(来自a的ABCDD,来自b的CDBDA)

如何解决这个问题?


更新

目标是从每个输入字符串中查找子字符串,以便它们具有相同的字符包。通过说“子串”,它们必须是连续的字符。


更新:最初我想到了一种动态编程方法。它的工作原理如下。

为了比较相同长度K的两袋字符,需要花费O(K)时间来实现。将每个字符串转换为缩短形式:

ABCDDEGF -> A1B1C1D2E1G1F1
FPCDBDAX -> A1B1C1D2F1P1X1

缩短形式是按字母表排序的字母表,后跟字符串中的频率数。 构造,排序和比较缩短形式总共需要O(K)时间。 (实现可以通过使用字符数组来实现)

如果两个字符的缩短形式具有相同的字符和相应的频率,则两个字符是相同的。

此外,需要O(logK)时间才能找到两个字符串之间的差异字符。

现在,对于两个输入字符串:

  1. 如果他们的缩短形式是相同的,那么这是最常见的字符包。
  2. 在string1中查找字符,使它们不出现在string2中。根据这些字符将string1标记为多个子字符串。
  3. 在string2中查找字符,使它们不出现在string1中。基于这些字符将字符串2标记为多个子字符串。
  4. 现在,我们有两个字符串列表。比较每一对(这与输入的较小尺寸的问题相同)并找到最长的常见字符包。
  5. 最坏的情况是O(N 3 ),最好的情况是O(N)。还有更好的主意吗?

4 个答案:

答案 0 :(得分:5)

创建a中存在的一组字符,以及b中存在的另一个字符。遍历每个字符串并敲击(例如,用一些其他不可能的值覆盖)所有不在另一个字符串集合中的字符。找到每个中剩余的最长字符串(即,只有“unstruck”字符的最长字符串)。

编辑:这是一个大致如上所述的解决方案,但是以一种特定于语言的方式(使用C ++语言环境/方面):

#include <string>
#include <vector>
#include <iostream>
#include <locale>
#include <sstream>
#include <memory>

struct filter : std::ctype<char> {
    filter(std::string const &a) : std::ctype<char>(table, false) {
        std::fill_n(table, std::ctype<char>::table_size, std::ctype_base::space);

        for (size_t i=0; i<a.size(); i++) 
            table[(unsigned char)a[i]] = std::ctype_base::upper;
    }
private:
    std::ctype_base::mask table[std::ctype<char>::table_size];
};

std::string get_longest(std::string const &input, std::string const &f) { 
    std::istringstream in(input);
    filter *filt = new filter(f);

    in.imbue(std::locale(std::locale(), filt));

    std::string temp, longest;

    while (in >> temp)
        if (temp.size() > longest.size())
            longest = temp;
    delete filt;
    return longest;
}

int main() { 
    std::string a = "ABCDDEGF",  b = "FPCDBDAX";
    std::cout << "A longest: " << get_longest(a, b) << "\n";
    std::cout << "B longest: " << get_longest(b, a) << "\n";
    return 0;
}

Edit2:我相信这种实现在所有情况下都是O(N)(每个字符串一次遍历)。这是基于std::ctype<char>使用查找表,即O(1)。使用哈希表,查找也会有O(1)预期复杂度,但O(N)最坏情况,因此整体复杂度将是O(N)预期,但O(N 2 )最坏情况。使用基于平衡树的集合,您将获得整体O(N lg N)。

答案 1 :(得分:3)

只是说明这个问题不会允许“贪婪”的解决方案,其中通过一次扩展现有的可行包一个元素来构建连续更大的包。原因是即使存在长度为k的袋子,也不需要任何可行的袋子长度(k-1),如下面的反例所示:

ABCD
CDAB

显然,这两个字符串共享一个长度为4的包(A:1, B:1, C:1, D:1),但没有共享的长度为3的包。这告诉我,这个问题可能很难。

答案 2 :(得分:1)

让我们看看这个问题..这个解决方案将更加优化,代码非常容易,但通过def阅读,你必须阅读代码才能获得想法。否则它会听起来很疯狂和复杂

关于此事的想法

在您的问题中,您提供的2个示例字符串可以将它们视为两个字符集,即{x,y,z}字符......

AND .. AND ...您生成的子字符串(集)将是两个字符串(集合)中常见的字符,并且将是连续条目和符合条件的字符串substring(ser)将是最大条目数

以上是结果的一些属性,但仅在通过以下算法\ methodolgy

使用时才有效

我们有两套

a = {BAHYJIKLO}

b = {YTSHYJLOP}

U b = { - , - ,H,Y,J, - , - ,L,O}

b U a = {Y, - , - ,H,Y,J,L,O, - }

只是我替换 没有资格获得工会集且“ - ”或任何特殊\被忽略的字符字符

这样做我们有两个字符串,我们可以从中轻松提取HYJLOYHYJLO

现在string \ substrings比较和不同的处理需要时间,所以我做的是我把这些字符串\ substrings写成一个文本文件,用空格或不同的行分隔..所以当我读取一个文件时,我得到整个字符串而不是有一个嵌套循环来定位子字符串或管理临时变量....

在您有HYJLOYHYJLO之后,我认为找到您想要的结果不会有问题....

注意:如果你开始用临时变量和嵌套循环处理字符串和子字符串,首先制作一个子字符串,然后搜索它...那么它将是非常昂贵的解决方案......你必须使用这样的归档......

char a[20], b[20]; //a[20] & b[30] are two strings
cin>>a; cin>>b;
int t=0;

open a temporary text file "file1" to write '(built-in-function works here)'
//a U b
for(int x=0; x<length(a); x++)
{
    t=0;

    for(int y=0; y<length(b); x++)
       { if( a[x] == b[y]) t=1; }

    if(t == 1)
       { 
          write 'a[x]' to the file1 '(built-in-function works here)'
          t=0;
       }
    else
       write a 'space' to the file1 '(built-in-function works here)'
}

//b U a
for(int x=0; x<length(a); x++)
{
    t=0;

    for(int y=0; y<length(b); x++)
       { if( b[x] == a[y]) t=1; }

    if(t == 1)
       {
         write 'a[x]' to the file1 '(built-in-function works here)'
         t=0;
       }
    else
       write a 'space' to the file1 '(built-in-function works here)'
}
/*output in the file wil be like this
_____FILE1.txt_____
  HYJ  LO Y HYJLO        
*/
//load all words in an array of stings from file '(built-in-function works here)'

char *words[]={"HYJ","LO","Y","HYJLO"};
int size=0,index=0;

for( int x=0; x<length(words); x++)
    for( int y=0; x<length(words); y++)
    {
       if( x!=y && words[x] is a substring of words[y] ) // '(built-in-function works here)'
          {
               if( length(words[x] ) < size )
               {
                     size = length(words[x];
                     index = x;
               }
          }
    }

 cout<< words[x]; 
 //its the desired result.. its pretty old school bu i think you get the idea

}

我写了代码...它的工作如果你想要它给我发送电子邮件我会发给你... b.t.w我喜欢这个问题而且这个算法的复杂性是3n(方形)

答案 3 :(得分:0)

这是我的反pythonic实现,但它利用了python的精彩内置集和字符串。

a = 'ABCDDEGF'
b = 'FPCDBDAX'

best_solution = None
best_solution_total_length = 0

def try_expand(a, b, a_loc, b_loc):
    # out of range checks
    if a_loc[0] < 0 or b_loc[0] < 0:
        return
    if a_loc[1] == len(a) or b_loc[1] == len(b):
        return


    if set(a[a_loc[0] : a_loc[1]]) == set(b[b_loc[0] : b_loc[1]]):
        global best_solution_total_length, best_solution
        #is this solution better than anything before it?
        if (len(a[a_loc[0] : a_loc[1]]) + len(b[b_loc[0] : b_loc[1]])) > best_solution_total_length:
            best_solution = (a_loc, b_loc)
            best_solution_total_length = len(a[a_loc[0] : a_loc[1]]) + len(b[b_loc[0] : b_loc[1]])


    try_expand(a, b, (a_loc[0]-1, a_loc[1]), (b_loc[0], b_loc[1]))
    try_expand(a, b, (a_loc[0], a_loc[1]+1), (b_loc[0], b_loc[1]))
    try_expand(a, b, (a_loc[0], a_loc[1]), (b_loc[0]-1, b_loc[1]))
    try_expand(a, b, (a_loc[0], a_loc[1]), (b_loc[0], b_loc[1]+1))


for a_i in range(len(a)):
    for b_i in range(len(b)):
        # starts of the recursive expansion from identical letters in two substrings
        if a[a_i] == b[b_i]:
            # if substrings were expanded from this range before then there won't be an answer there
            if best_solution == None or best_solution[0][0] > a_i or best_solution[0][1] <= a_i or best_solution[1][0] > b_i or best_solution[1][1] <= b_i:
                    try_expand(a, b, (a_i, a_i), (b_i, b_i))


print a[best_solution[0][0] : best_solution[0][1]], b[best_solution[1][0] : best_solution[1][1]]

忘了提一下,这显然是一种相当强大的方法,我确信有一种运行速度快得多的算法。