您的位置:首页 > 其它

查找两个字符串a,b中的最长公共子串

2016-06-27 17:46 1121 查看



题目描述

查找两个字符串a,b中的最长公共子串 
输入描述:
输入两个字符串

输出描述:
返回重复出现的字符

输入例子:
abcdefghijklmnop
abcsafjklmnopqrstuvw


输出例子:

jklmnop

解法一:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNext()){
String s1 = in.nextLine();
String s2 = in.nextLine();
String max = s1.length() >= s2.length()?s1:s2;
String min = s1.length() >= s2.length()?s2:s1;
int l = 0;
String s ="";
for(int i=0;i<min.length();i++){
for(int j=i+1;j<=min.length();j++){
if(max.contains(min.substring(i,j)) && j-i>l){
l=j-i;
s=min.substring(i,j);
}
}
}
System.out.println(s);
}

}

}


解法二:
012算法笔记——【动态规划】最长公共子串问题

   1、问题相关定义

          (1)字符串:一个字符串S是将n 个字符顺次排列形成的数组, n称为S的长度,表示为len(S) 。S的第i字符表示为S[ i ]。

      (2)子串:字符串S的子串S[ i:j ] ( i ≤ j)表示S串中从i到j这一段,也就是排列S[ i ] , S[ i + 1 ] , ⋯,S[ j ] 形成的字符串。

      (3)后缀:后缀是指从某个位置i开始到整个串末尾结束的一个特殊子串。字符串S的从i开头的后缀表示为Suffix( S,i) ,也就是Suffix( S, i) = S[ i:len ( S) ]。

      (4)公共后缀:字符串U 如果既是字符串S的后缀又是字符串T的后缀,则字符串U 是字符串S和T的一个公共后缀。

      (5)最长公共后缀:字符串S和T的最长公共后缀是指字符串S和T的所有公共后缀中长度最大的子串。

      (4)公共子串:字符串U 如果既是字符串S的子串又是字符串T的子串,则字符串U 是字符串S和T的一个公共子串。

      (5)最长公共子串:字符串S和T的最长公共子串是指字符串S和T的所有公共子串中长度最大的子串。 

      例:给定2 个长度分别为4, 4 的字符串“abab”,“baba”,它们的公共子串(Common Substring) 有“”,“a”,“b”,“ab”,“ba”,“aba”,“bab”。其中最长公共子串(LCS) 即为“aba”或“bab”。

          2、动态规划求解

      在使用动态规划求解2个长度分别为p, q的字符串S,T的最长公共子串问题前,先给出求它们任意前缀子串对S[1:i],T[1:j]的最长公共后缀的算法,其中:1 ≤ i ≤ p,1 ≤ j ≤ q。设LCSuffix(S[1:i],T[1:j])表示前缀子串对S[1: i] ,T[1:j] 的最长公共后缀,则该问题的递推关系式如下:



 例如:字符串S“baba”, T“abab”, 使用上面递推关系式求得字符串S, T所有前缀子串对的最长公共后缀,如下表所示:



字符串S, T所有前缀子串对应的最长公共后缀中长度最大的即为字符串S, T的最长公共子串,即:

其中LCS(
S,T) 表示字符串S, T的最长公共子串。对于字符串S“baba”, T“abab”,从表1中可以看出它们的最长公共后缀中长度最大为3,对应两个最长公共子串,即:"aba"和"bab"。 

// CPPPRO.cpp : 定义控制台应用程序的入口点。
//3m6 最长公共子串,动态规划实现
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

string getLCSLength(string &s, string &t);

int main()
{
string s,t;
cout<<"请输入字符串s:"<<endl;
cin>>s;
cout<<"请输入字符串t:"<<endl;
cin>>t;
cout<<"最长公共子串为:"<<endl;
cout<<getLCSLength(s,t)<<endl;
return 0;
}

string getLCSLength(string &s, string &t)
{
int p = s.length();
int q = t.length();

string **num = new string *[p];
for(int i=0;i<p;i++)
{
num[i] = new string[q];
}

char char1 = '\0';
char char2 = '\0';

int len = 0;
string lcs = "" ;

for(int i=0; i<p; i++)
{
for (int j=0; j<t.length(); j++)
{
char1 = s.at(i);
char2 = t.at(j);
if(char1!=char2)
{
num[i][j] = "" ;
}
else
{
if (i==0||j==0)
{
num[i][j]=char1;
}
else
{
num[i][j]=num[i-1][j-1]+char1;
}
if(num[i][j].length()>len)
{
len = num[i][j].length() ;
lcs = num[i][j];
}
else if(num[i][j].length()==len)
{
lcs = lcs + "," + num[i][j];
}
}
}
}

for(int i=0;i<p;i++)
{
delete[] num[i];
}
delete[] num;

return lcs;
}<span style="font-family:Arial;color:#333333;"><span style="font-size: 14px; line-height: 26px;">
</span></span>


程序运行结果如下:



 使用动态规划算法求2个长度分别为p, q的字符串S, T的最长公共子串需要构造一个二维表,该二维表含有pq个项,使用递推的方法求每项的时间负责度为O (1) ,所以求出所有项的时间复杂度为O (pq) 。在二维表中只需找到一个最大值项,其对应于原2个字符串的最长公共子串,因此使用动态规划算法的时间复杂度为O (pq) 。

          建立2个长度分别为p, q的字符串S, T的广义后缀数组的时间复杂度为O (p + q) ,求广义后缀数组中相邻子串的最长公共前缀的时间复杂度为<O(p + q) ,O(1)>,即需要预处理时间复杂度为O(p + q) ,每次查询最长公共前缀的时间复杂度为O(1) 。遍历所有最长公共前缀的时间复杂度为O(p+q) ,因此使用广义后缀数组解决最长公共子串的时间复杂度为O(p+q) 。对于字符串S, T,它的广义后缀数组在不降低性能同时需要使用2
(p + q) 空间。

      解决2个长度分别为p, q的字符串最长公共子串问题的动态规划算法,广义后缀树算法以及广义后缀数组算法在时间复杂度,空间复杂度以及编程实现上的比较分析总结如下表所示:



    其中动态规划算法以及广义后缀树算法的研究已经非常成熟,在计算机算法领域有着各种各样的应用,但在解决字符串最长公共子串问题上还有一定不足,即动态规划算法占用的时间复杂度以及空间复杂度都很高,而广义后缀树算法虽然降低了时间复杂度,但其空间复杂度还是较高,另外编程实现也较难。虽然广义后缀数组的理论研究还在发展中,但就目前的研究成果而言,在解决字符串最长公共子串问题上,根据上面给出的算法,它可以综合动态规划算法以及广义后缀树算法的优点,既保证了线性的时间复杂度,较低的空间复杂度又易于编程实现。改进的后缀数组算法及相关定义应用可查看博文《最长公共子串问题的后缀数组解法》。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: