如何找出两个字符串中最长的相同子串?

时间:2023-02-16 20:21:02
写压缩算法,用的LZSS算法。但遇到如题这个问题的时候就卡着了,怎么办呢?
在网上看到有个LCS算法,但并不适用于上面的算法啊,因为字符串buffer有8K!
现在我用的哈希表,但又出现一个问题:得到了匹配串,但哈希表的更新又很麻烦---要改变每个表值所对应字符串的地址.(不知道我这样说能不能理解)

哪位有办法啊?急啊....

16 个解决方案

#1


字符串1: bcgghadcced

      字符串2:  aggcadcbhe

      最长共同子字符串: adc

      算法思想:建立一个二维矩阵,二维矩阵的大小分别是这两个字符串的长度,然后对二位矩阵进行赋值,赋值原则是这样的,将其中一个字符串的每一个字母与另一个字符串中的每一个字母进行比较,如果字符相同,则赋值为1,否则赋值为0,对矩阵赋值后,从矩阵的右上角开始,寻找斜线方向上所有数字和最大的那一条线,此时连续的几个1就标志了最大的子字符串。 矩阵示意图如下:

        b  c  g  g  h  a  d  c  c  e  d

    a  0  0  0  0  0  1  0  0  0  0  0

    g  0  0  1  1  0  0  0  0  0  0  0

    g  0  0  1  1  0  0  0  0  0  0   0 

    c  0  1  0  0  0  0  0  1  1  0  0

    a  0  0  0  0  0  1 0  0  0  0  0

    d  0  0  0  0  0  0  1  0  0  0  0

    c  0  1  0  0  0  0  0  1  0  0  0 

    b 1   0  0  0  0  0  0  0  0  0  0

    h 0   0  0  0  1  0  0  0  0  0  0

    e 0  0  0   0  0  0   0   0  0  1  0

  部分代码如下:

 // comparsion
     for(int n = 0; n< la; n++)
   for(int m = 0; m< lb ; m++)
   {
    if(a[n] == b[m])
    {
     if(m == 0 || n == 0) 
       ab[n][m] = 1;
     else
       ab[n][m] = ab[n-1][m-1] +1;
    }
    else 
     ab[n][m] = 0;
   }
  // result for comparsion 
   for(i =0;i   {
    for(j =0 ;j    {
                 printf("%d ",ab[i][j]);
    }
    printf("\n");
   }
     // get common result string;
   int max = 0,index=0;
   int maxa[MAXCH]; 
   memset(maxa,0,MAXCH);
   for(i = 0; i   {
    max = 0;
    for(j = 0;j    {
     if(ab[i][j]>max)
     {
      maxa[i] = ab[i][j],
         max = ab[i][j];
     }
              
    }
   }
   for(i=0;i    printf("%d \n",maxa[i]);
          max = 0;
    for(i = 0;i    {
     if(maxa[i] > max)
     {
      max = maxa[i];
      index = i;
     }
    }

     获得最长子字符串:

     for(i=0;i    {
     printf("%c",a[index-max+1+i]);
    }

#2



char* GetSubstring(char* strSource) 

char* strSubstring; //用于保存得到的子串,大小在找到最大子串后再确定,作为返回值 
int nLen; //源字符串长度 
int nCurPos; //当前统计字符串的头指针位置(相对于原字符串第一个字符的位置) 
int nCurCount; //当前统计字符串的长度(有相同字符组成的子字符串) 
int nPos; //当前最长的子串的头指针位置 
int nCount; //当前最长的子串的长度 

nLen = strlen(strSource); 

//初始化变量 
nCount = 1; 
nPos = 0; 
nCurCount = 1; 
nCurPos = 0; 

//遍历整个字符串 
for(i = 1; i < nLen; i++) 

if(strSource[i] == strSource[nCurPos])//如果当前字符与子串的字符相同,子串长度增1 
nCurCount++; 
else //如果不相同,开始统计新的子串 

if(nCurCount > nCount)//如果当前子串比原先最长的子串长,把当前子串信息(起始位置 + 长度)保留下来 

nCount = nCurCount; 
nPos = nCurPos; 

//长度复值为1,新串的开始位置为i 
nCurCount = 1; 
nCurPos = i; 



//为返回的子串分配空间(长度为nCount,由于要包括字符串结束符\0,故大小要加1) 
strSubstring = (char*)malloc(nCount + 1); 

//复制子串(用其他函数也可以) 
for(i = 0; i < nCount; i++) 
strSubstring[i] = strSource[nPos + i]; 
strSubstring[nCount] = '\0'; 

return strSubstring; 


//不太明白你的“输出”究竟是什么意思,只好给段示意性代码 
main() 

//输入一个字符串strSource 

char* strSubstring = GetSubstring(strSource); 

printf("最长子串为:%s\n长度为:%d",strSubstring,strlen(strSubstring)); 

//释放strSubstring 
free(strSubstring); 

#3


lingyin55的算法应该是对的。
可以改进的地方主要是那个二维数组。8k * 8k * sizeof(int) 是有点大了。其实只要保留最近的两行就足够了。

#4


最大子串,要高效实现还蛮有难度的。
http://topic.csdn.net/u/20071206/20/8bb17656-f52f-4ad2-879e-16e25b864fcf.html

可以参考下这个DP解。

#5


lingyin55 说的是LCS算法,这个我知道....(还是谢谢!)
但字符数组太大了!而且还有一个我没有说:有一个字符数组的大小是不确定的..
有谁写过LZSS压缩算法吗?我的问题就出在滑动窗口那儿!

#6



#define   BUFFSIZE   1024   
    
  void   main()   
  {   
          char   sz1[BUFFSIZE]   =   {"abractyeyt"};   
          char   sz2[BUFFSIZE]   =   {"dgdsaeactyey"};   
          char   sz3[BUFFSIZE];   
          char   sz4[BUFFSIZE];   
    
          char   ch1,   ch2;   
          int   i   =   0;   
          int   j   =   0;   
          int   k   =   0;   
    
          for   (i   =   0;   i   <   strlen(sz1);   i++)     
          {   
                  ch1   =   sz1[i];   
                  for   (j   =   0;   j   <   strlen(sz2);   j++)   
                  {   
                          if   (ch1   =   sz2[j])     
                          {   
                                  for   (k   =   0;   (k+i)   <   strlen(sz1)   &&   (k+j)   <   strlen(sz2);   k++)   
                                  {   
                                          if   (sz1[i+k]   ==   sz2[j+k])   {   
                                                  sz3[k]   =   sz1[i+k];   
                                          }   
                                          else   {   
                                                  break;   
                                          }   
                                  }   
                          }   
                          if   (strlen(sz3)   >   strlen(sz4))   
                          {   
                                  strcpy(sz4,   sz3);   
                          }   
                          strcpy(sz3,   "");   
                  }   
          }   
            
          printf("sz3   is   %s\n",   sz3);   
          printf("sz4   is   %s\n",   sz4);   


#7


lingyin55的算法中,要求出ab[n][m],只需要知道ab[n-1][m-1]。
如果是用滑动的做法,那么只要m已知,就可以让n滑动。
对于任意的第n行,只需保留上一行就可以了。空间上应该是可行的做法。

#8


1.将两个字符串相连,其中,连接部分放一个在两个字符串中都没有出现的字符.
2.构造这个串的后缀数组.
3.求后缀的最长公共前缀.

其中用倍增法构造后缀数组:O(nlogn),用DC3算法是O(n)
求后缀的最长公共前缀:O(n)
时间复杂度可以做到O(n)
你把8k换成8万都没有问题.
空间复杂度是O(n).

更多情况参考:
许智磊2004.pdf
的文章.

IOI2004国家集训队论文 许智磊
第 1 页 共11页
后 缀 数 组
安徽省芜湖市第一中学 许智磊
【摘要】
本文介绍后缀数组的基本概念、方法以及应用。
首先介绍O(nlogn)复杂度构造后缀数组的倍增算法,接着介绍了配合后缀
数组的最长公共前缀 LCP(Longest Common Prefix)的计算方法,并给出一个
线性时间内计算height 数组(记录跨度为1 的LCP 值的数组)的算法。为了让
读者对如何运用后缀数组有一个感性认识,还介绍了两个应用后缀数组的例子:
多模式串的模式匹配(给出每次匹配O(m+logn)时间复杂度的算法)以及求最
长回文子串(给出O(nlogn)时间复杂度的算法)。最后对后缀数组和后缀树作了
一番比较。
【关键字】
字符串 后缀 k-前缀比较关系
后缀数组 名次数组 后缀树 倍增算法 基数排序
最长公共前缀 RMQ问题 模式匹配 回文串 最长回文子串

#9


UP

#10


方法很多啊。找个指针挨个检查也行啊。

#11


果然是高手

#12


学习了

#13


问题解决了,不过是用的哈希表...
如果用LCS算法(1楼算法),窗口滑动的问题很难解决。(我在用LZSS算法写压缩算法)
不过还是感谢各位!!
给分了!!!

#14


up

#15


up

#16


8楼的方法不行啊,会匹配到自己的
比如:
str1 = "abcd" ;
str2 = "cdefcdef"
8楼的意思是求解str3 = "abcd#cdefcdef"的最长匹配子串,这个结果是"cdef",但是正确结果是"cd"

#1


字符串1: bcgghadcced

      字符串2:  aggcadcbhe

      最长共同子字符串: adc

      算法思想:建立一个二维矩阵,二维矩阵的大小分别是这两个字符串的长度,然后对二位矩阵进行赋值,赋值原则是这样的,将其中一个字符串的每一个字母与另一个字符串中的每一个字母进行比较,如果字符相同,则赋值为1,否则赋值为0,对矩阵赋值后,从矩阵的右上角开始,寻找斜线方向上所有数字和最大的那一条线,此时连续的几个1就标志了最大的子字符串。 矩阵示意图如下:

        b  c  g  g  h  a  d  c  c  e  d

    a  0  0  0  0  0  1  0  0  0  0  0

    g  0  0  1  1  0  0  0  0  0  0  0

    g  0  0  1  1  0  0  0  0  0  0   0 

    c  0  1  0  0  0  0  0  1  1  0  0

    a  0  0  0  0  0  1 0  0  0  0  0

    d  0  0  0  0  0  0  1  0  0  0  0

    c  0  1  0  0  0  0  0  1  0  0  0 

    b 1   0  0  0  0  0  0  0  0  0  0

    h 0   0  0  0  1  0  0  0  0  0  0

    e 0  0  0   0  0  0   0   0  0  1  0

  部分代码如下:

 // comparsion
     for(int n = 0; n< la; n++)
   for(int m = 0; m< lb ; m++)
   {
    if(a[n] == b[m])
    {
     if(m == 0 || n == 0) 
       ab[n][m] = 1;
     else
       ab[n][m] = ab[n-1][m-1] +1;
    }
    else 
     ab[n][m] = 0;
   }
  // result for comparsion 
   for(i =0;i   {
    for(j =0 ;j    {
                 printf("%d ",ab[i][j]);
    }
    printf("\n");
   }
     // get common result string;
   int max = 0,index=0;
   int maxa[MAXCH]; 
   memset(maxa,0,MAXCH);
   for(i = 0; i   {
    max = 0;
    for(j = 0;j    {
     if(ab[i][j]>max)
     {
      maxa[i] = ab[i][j],
         max = ab[i][j];
     }
              
    }
   }
   for(i=0;i    printf("%d \n",maxa[i]);
          max = 0;
    for(i = 0;i    {
     if(maxa[i] > max)
     {
      max = maxa[i];
      index = i;
     }
    }

     获得最长子字符串:

     for(i=0;i    {
     printf("%c",a[index-max+1+i]);
    }

#2



char* GetSubstring(char* strSource) 

char* strSubstring; //用于保存得到的子串,大小在找到最大子串后再确定,作为返回值 
int nLen; //源字符串长度 
int nCurPos; //当前统计字符串的头指针位置(相对于原字符串第一个字符的位置) 
int nCurCount; //当前统计字符串的长度(有相同字符组成的子字符串) 
int nPos; //当前最长的子串的头指针位置 
int nCount; //当前最长的子串的长度 

nLen = strlen(strSource); 

//初始化变量 
nCount = 1; 
nPos = 0; 
nCurCount = 1; 
nCurPos = 0; 

//遍历整个字符串 
for(i = 1; i < nLen; i++) 

if(strSource[i] == strSource[nCurPos])//如果当前字符与子串的字符相同,子串长度增1 
nCurCount++; 
else //如果不相同,开始统计新的子串 

if(nCurCount > nCount)//如果当前子串比原先最长的子串长,把当前子串信息(起始位置 + 长度)保留下来 

nCount = nCurCount; 
nPos = nCurPos; 

//长度复值为1,新串的开始位置为i 
nCurCount = 1; 
nCurPos = i; 



//为返回的子串分配空间(长度为nCount,由于要包括字符串结束符\0,故大小要加1) 
strSubstring = (char*)malloc(nCount + 1); 

//复制子串(用其他函数也可以) 
for(i = 0; i < nCount; i++) 
strSubstring[i] = strSource[nPos + i]; 
strSubstring[nCount] = '\0'; 

return strSubstring; 


//不太明白你的“输出”究竟是什么意思,只好给段示意性代码 
main() 

//输入一个字符串strSource 

char* strSubstring = GetSubstring(strSource); 

printf("最长子串为:%s\n长度为:%d",strSubstring,strlen(strSubstring)); 

//释放strSubstring 
free(strSubstring); 

#3


lingyin55的算法应该是对的。
可以改进的地方主要是那个二维数组。8k * 8k * sizeof(int) 是有点大了。其实只要保留最近的两行就足够了。

#4


最大子串,要高效实现还蛮有难度的。
http://topic.csdn.net/u/20071206/20/8bb17656-f52f-4ad2-879e-16e25b864fcf.html

可以参考下这个DP解。

#5


lingyin55 说的是LCS算法,这个我知道....(还是谢谢!)
但字符数组太大了!而且还有一个我没有说:有一个字符数组的大小是不确定的..
有谁写过LZSS压缩算法吗?我的问题就出在滑动窗口那儿!

#6



#define   BUFFSIZE   1024   
    
  void   main()   
  {   
          char   sz1[BUFFSIZE]   =   {"abractyeyt"};   
          char   sz2[BUFFSIZE]   =   {"dgdsaeactyey"};   
          char   sz3[BUFFSIZE];   
          char   sz4[BUFFSIZE];   
    
          char   ch1,   ch2;   
          int   i   =   0;   
          int   j   =   0;   
          int   k   =   0;   
    
          for   (i   =   0;   i   <   strlen(sz1);   i++)     
          {   
                  ch1   =   sz1[i];   
                  for   (j   =   0;   j   <   strlen(sz2);   j++)   
                  {   
                          if   (ch1   =   sz2[j])     
                          {   
                                  for   (k   =   0;   (k+i)   <   strlen(sz1)   &&   (k+j)   <   strlen(sz2);   k++)   
                                  {   
                                          if   (sz1[i+k]   ==   sz2[j+k])   {   
                                                  sz3[k]   =   sz1[i+k];   
                                          }   
                                          else   {   
                                                  break;   
                                          }   
                                  }   
                          }   
                          if   (strlen(sz3)   >   strlen(sz4))   
                          {   
                                  strcpy(sz4,   sz3);   
                          }   
                          strcpy(sz3,   "");   
                  }   
          }   
            
          printf("sz3   is   %s\n",   sz3);   
          printf("sz4   is   %s\n",   sz4);   


#7


lingyin55的算法中,要求出ab[n][m],只需要知道ab[n-1][m-1]。
如果是用滑动的做法,那么只要m已知,就可以让n滑动。
对于任意的第n行,只需保留上一行就可以了。空间上应该是可行的做法。

#8


1.将两个字符串相连,其中,连接部分放一个在两个字符串中都没有出现的字符.
2.构造这个串的后缀数组.
3.求后缀的最长公共前缀.

其中用倍增法构造后缀数组:O(nlogn),用DC3算法是O(n)
求后缀的最长公共前缀:O(n)
时间复杂度可以做到O(n)
你把8k换成8万都没有问题.
空间复杂度是O(n).

更多情况参考:
许智磊2004.pdf
的文章.

IOI2004国家集训队论文 许智磊
第 1 页 共11页
后 缀 数 组
安徽省芜湖市第一中学 许智磊
【摘要】
本文介绍后缀数组的基本概念、方法以及应用。
首先介绍O(nlogn)复杂度构造后缀数组的倍增算法,接着介绍了配合后缀
数组的最长公共前缀 LCP(Longest Common Prefix)的计算方法,并给出一个
线性时间内计算height 数组(记录跨度为1 的LCP 值的数组)的算法。为了让
读者对如何运用后缀数组有一个感性认识,还介绍了两个应用后缀数组的例子:
多模式串的模式匹配(给出每次匹配O(m+logn)时间复杂度的算法)以及求最
长回文子串(给出O(nlogn)时间复杂度的算法)。最后对后缀数组和后缀树作了
一番比较。
【关键字】
字符串 后缀 k-前缀比较关系
后缀数组 名次数组 后缀树 倍增算法 基数排序
最长公共前缀 RMQ问题 模式匹配 回文串 最长回文子串

#9


UP

#10


方法很多啊。找个指针挨个检查也行啊。

#11


果然是高手

#12


学习了

#13


问题解决了,不过是用的哈希表...
如果用LCS算法(1楼算法),窗口滑动的问题很难解决。(我在用LZSS算法写压缩算法)
不过还是感谢各位!!
给分了!!!

#14


up

#15


up

#16


8楼的方法不行啊,会匹配到自己的
比如:
str1 = "abcd" ;
str2 = "cdefcdef"
8楼的意思是求解str3 = "abcd#cdefcdef"的最长匹配子串,这个结果是"cdef",但是正确结果是"cd"