求两个字符串最长公共子串LCS

本文介绍了解决最长公共子串问题的LCS算法。通过构造特殊矩阵并利用标记变量记录最大匹配,实现快速查找两个字符串间的最长公共子串。文章详细展示了矩阵生成过程,并提供了具体示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。

比如:

String str1 = new String("adbccadebbca");
String str2 = new String("edabccadece");
str1与str2的公共子串就是bccade.

解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232


  0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
  0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
  0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
  0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。下面是新的矩阵生成方式:
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
  0 1 0 0 0 0 0 0 0 2 1 0 0 0 0
  1 0 2 0 1 0 1 0 0 0 0 0 1 0 0
  0 2 0 0 0 0 0 0 0 1 1 0 0 0 0
  1 0 3 0 1 0 1 0 0 0 0 0 1 0 0
  0 0 0 4 0 0 0 2 1 0 0 1 0 0 0
  1 0 1 0 5 0 1 0 0 0 0 0 2 0 0
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
  0 0 0 2 0 0 0 2 1 0 0 1 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
  0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。

  这样做速度比较快,但是花的空间太多。我们注意到在改进的矩阵生成方式当中,每生成一行,前面的那一行就已经没有用了。因此我们只需使用一维数组即可。最终的代码如下:

publicclassLCString2...{

publicstaticvoidgetLCString(char[]str1,char[]str2)
...{
inti,j;
intlen1,len2;
len1
=str1.length;
len2
=str2.length;
intmaxLen=len1>len2?len1:len2;
int[]max=newint[maxLen];
int[]maxIndex=newint[maxLen];
int[]c=newint[maxLen];

for(i=0;i<len2;i++)
...{
for(j=len1-1;j>=0;j--)
...{
if(str2[i]==str1[j])
...{
if((i==0)||(j==0))
c[j]
=1;
else
c[j]
=c[j-1]+1;
}

else
...{
c[j]
=0;
}


if(c[j]>max[0])
...{//如果是大于那暂时只有一个是最长的,而且要把后面的清0;
max[0]=c[j];
maxIndex[
0]=j;

for(intk=1;k<maxLen;k++)
...{
max[k]
=0;
maxIndex[k]
=0;
}

}

elseif(c[j]==max[0])
...{//有多个是相同长度的子串
for(intk=1;k<maxLen;k++)
...{
if(max[k]==0)
...{
max[k]
=c[j];
maxIndex[k]
=j;
break;//在后面加一个就要退出循环了
}


}

}

}

}


for(j=0;j<maxLen;j++)
...{
if(max[j]>0)
...{
System.out.println(
""+(j+1)+"个公共子串:");
for(i=maxIndex[j]-max[j]+1;i<=maxIndex[j];i++)
System.out.print(str1[i]);
System.out.println(
"");
}

}

}


publicstaticvoidmain(String[]args)...{

Stringstr1
=newString("adbba1234");
Stringstr2
=newString("adbbf1234sa");
getLCString(str1.toCharArray(),str2.toCharArray());
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值