最长公共子串(LCS:Longest Common Substring)
最长公共子串(LCS:Longest Common Substring)是一个非常经典的面试题目,本人在乐视二面中被面试官问过,惨败在该题目中。
什么是最长公共子串
最长公共子串问题的基本表述为:给定两个字符串,求出它们之间最长的相同子字符串的长度。
最直接的解法就是暴力解法:遍历所有子字符串,比较它们是否相同,然后去的相同子串中最长的那个。对于长度为n的字符串,它子串的数量为n(n-1)/2,假如两个字符串长度均为n,那么该解法的复杂度为O(n^4),想想并不是取出所有的子串,那么该解法的复杂度为O(n^3)。
复杂度太高,可以进行优化,可以利用动态规划法(有重叠的子问题)。
暴力解法
对于该问题,直接的思路就是要什么就找什么,要子串就要子串,要相同就比较每个字符,要长度就计算长度,所以很容易写出下列代码:
//暴力
public static int longestCommonSubstring(String s1, String s2){
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int str1_length = str1.length;
int str2_length = str2.length;
if(str1_length == 0 || str2_length == 0)
return 0;
//最大长度
int maxLength = 0;
int compareNum = 0;
int start1 = -1;
int start2 = -1;
for(int i=0;i<str1_length;i++){
for(int j=0;j<str2_length;j++){
int m = i;
int n = j;
//相同子串长度
int length = 0;
while(m < str1_length && n < str2_length){
compareNum++;
if(str1[m] != str2[n])
break;
m++;
n++;
length++;
}
if(length > maxLength){
maxLength = length;
start1 = i + 1;
start2 = j + 1;
} }
}
System.out.println("比较次数" + compareNum + ",s1起始位置:" + start1 + ",s2起始位置:" + start2);
return maxLength;
}
该思路以字符串中每个字符作为子串的开始,判断以此开始的子串的相同字符所能达到的最大长度。从上述代码来看,复杂度是O(n^2),但是在比较两个相同开端的子串的效率不是O(1),是O(n),所以上述算法的复杂度为O(n^3)。
动态规划-空间换时间
上述解法回答面试官,面试官肯定会让你优化!
我们发现,在相同开端的子串的比较中,有很多事重复动作。比如在比较以i,j分别为起点的子串时,有可能会进行i+1和j+1以及i+2和j+2位置的字符的比较。而以i+1,j+1分别为起点的子串时,这些字符又被比较了一次。也就说该问题有非常相似的子问题,而子问题之间又有重叠,这就给动态规划法创造了契机。
暴力解法是以子串开端开始寻找,现在换个思路,以相同子串的字符结尾来利用动态规划法。
假设两个字符串分别为A、B,A[i]和B[j]分别表示其第i和j个字符,再假设K[i,j]表示以A[i]和B[j]结尾的子串的最大长度。那么A,B分别再向下走一个字符,我们可以推断出K[i+1,j+1]与K[i,j]之间的关系,如果A[i] == B[j],那么K[i+1,j+1] = K[i,j] + 1;否则K[i+1,j+1] =0。而如果A[i+1]和B[j+1]相同,那么就只要在以A[i]和B[j]结尾的最长相同子串之后分别添上这两个字符即可,这样就可以让长度增加一位,综上所述,就是K[i+1,j+1] = (A[i] == B[j] ? K[i,j] + 1 : 0)的关系。
由上述K[i+1,j+1] = (A[i] == B[j] ? K[i,j] + 1 : 0)的关系,想到了使用二维数组来存储两个字符串之间的相同子串关系,因为K[i+1,j+1] = (A[i+1] == B[j+1] ? K[i,j] + 1 : 0)关系,只计算二维数据的最上列和最左列数值即可,其他数值通过K[i+1,j+1] = (A[i+1] == B[j+1] ? K[i,j] + 1 : 0)可得。如下图所示:

代码如下:
//优化
public static int longestCommonSubstring1(String s1, String s2){
if(s1.length() == 0 || s2.length() == 0)
return 0;
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int start1 = -1;
int start2 = -1;
int[][] results = new int[str2.length][str1.length];
//最大长度
int maxLength = 0;
int compareNum = 0;
for(int i=0;i<str1.length;i++){
results[0][i] = (str2[0] == str1[i] ? 1 : 0);
compareNum++;
for(int j=1;j<str2.length;j++){
results[j][0] = (str1[0] == str2[j] ? 1 : 0);
if(i>0 && j>0){
if(str1[i] == str2[j]){
results[j][i] = results[j-1][i-1] + 1;
compareNum++;
}
}
if(maxLength < results[j][i]){
maxLength = results[j][i];
start1 = i - maxLength + 2;
start2 = j - maxLength + 2;
}
}
}
System.out.println("比较次数" + (compareNum+str2.length) + ",s1起始位置:" + start1 + ",s2起始位置:" + start2);
return maxLength;
}
用二维数组保存计算结果,避免了重复计算,运算的时间复杂度降低到了O(n^2)。
最长公共子串(LCS:Longest Common Substring)的更多相关文章
- 最长公共子串算法(Longest Common Substring)
给两个字符串,求两个字符串的最长子串 (例如:"abc""xyz"的最长子串为空字符串,"abcde"和"bcde"的最 ...
- 动态规划之最长公共子序列LCS(Longest Common Subsequence)
一.问题描述 由于最长公共子序列LCS是一个比较经典的问题,主要是采用动态规划(DP)算法去实现,理论方面的讲述也非常详尽,本文重点是程序的实现部分,所以理论方面的解释主要看这篇博客:http://b ...
- 动态规划求最长公共子序列(Longest Common Subsequence, LCS)
1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...
- 后缀自动机(SAM) :SPOJ LCS - Longest Common Substring
LCS - Longest Common Substring no tags A string is finite sequence of characters over a non-empty f ...
- spoj 1811 LCS - Longest Common Substring (后缀自己主动机)
spoj 1811 LCS - Longest Common Substring 题意: 给出两个串S, T, 求最长公共子串. 限制: |S|, |T| <= 1e5 思路: dp O(n^2 ...
- 【SP1811】LCS - Longest Common Substring
[SP1811]LCS - Longest Common Substring 题面 洛谷 题解 建好后缀自动机后从初始状态沿着现在的边匹配, 如果失配则跳它的后缀链接,因为你跳后缀链接到达的\(End ...
- spoj1811 LCS - Longest Common Substring
地址:http://www.spoj.com/problems/LCS/ 题面: LCS - Longest Common Substring no tags A string is finite ...
- 最长公共子串LCS(Longest Common Substring)
一.问题描述 寻求两个字符串中的最大公共字串,其中子串是指字符串中连续的字符组成的,而不是像子序列,按照字符的前后顺序组成.如str1="sgabacbadfgbacst",str ...
- 「双串最长公共子串」SP1811 LCS - Longest Common Substring
知识点: SAM,SA,单调栈,Hash 原题面 Luogu 来自 poj 的双倍经验 简述 给定两字符串 \(S_1, S_2\),求它们的最长公共子串长度. \(|S_1|,|S_2|\le 2. ...
随机推荐
- DOM相关
归纳一下, 不管是DOM Core还是HTML-DOM,我们在使用JavaScript的时候要注意浏览器之间的兼容性,因为不同的浏览器对这两类方法和属性的支持可能不一样,一般推荐使用DOM Core方 ...
- 【 js 基础 】【 源码学习 】柯里化和箭头函数
最近在看 redux 的源码,代码结构很简单,主要就是6个文件,其中 index.js 负责将剩余5个文件中定义的方法 export 出来,其他5个文件各自负责一个方法的实现. 大部分代码比较简单,很 ...
- session与cookie的区别和用法
一.session 1.保存在服务器的,每个人存一份2.可以存储任何类型数据3.有一个默认过期时间注意:在所有使用session的页面最顶端要开启session---session_start();存 ...
- Oracle 参数文件及相关操作介绍
Oracle 参数文件及相关操作介绍 by:授客 QQ:1033553122 1.服务器参数文件 服务器参数文件是一个二进制文件,作为初始化参数的存储仓库.实例运行时,可用ALTER SYSTEM来改 ...
- StretchBlt和StretchDIBits
StretchBlt:从源矩形中复制一个位图到目标矩形,必要时按目标设备设置的模式进行图像的拉伸或压缩,如果目标设备是窗口DC,则意味着在窗口绘制位图,大致的使用代码如下: void DrawImag ...
- JavaScript语法详解:运算符和表达式
本文首发于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. 我们在上一篇文章里讲到了JS中变量的概念,本篇文章讲一下运算符和表达式. 比 ...
- Mongodb集群与分片 2
前面我们介绍了简单的集群配置实例.在简单实例中,虽然MongoDB auto-Sharding解决了海量存储问题,和动态扩容问题,但是离我们在真实环境下面所需要的高可靠性和高可用性还有一定的距离. 下 ...
- kafka入门1:安装及配置
1下载 官方下载地址:https://kafka.apache.org/downloads 案例使用版本:kafka_2.11-1.1.0.tgz 2上传服务器 使用ftp工具将压缩包放置到服务器上 ...
- 用于创建和管理 Azure 虚拟机的常用 PowerShell 命令
本文介绍一些可用于在 Azure 订阅中创建和管理虚拟机的 Azure PowerShell 命令. 若要获取特定命令行开关和选项的详细帮助,可以使用 Get-Help 命令. 有关安装最新版 Azu ...
- 转:C# 读取EXCEL文件的三种经典方法
1.方法一:采用OleDB读取EXCEL文件: 把EXCEL文件当做一个数据源来进行数据的读取操作,实例如下: public DataSet ExcelToDS(string Path) { stri ...