除代码外,其他文字部分均为直接转载。代码为参考。
文章作者: Yx.Ac   文章来源: 勇幸|Thinking ( http://www.ahathinking.com)   转载请注明,谢谢合作。

---

这个LCS跟前面说的最长公共子序列的LCS不一样,不过也算是LCS的一个变体,在LCS中,子序列是不必要求连续的,而子串则是“连续”的。即:

题:给定两个字符串X,Y,求二者最长的公共子串,例如X=[aaaba],Y=[abaa]。二者的最长公共子串为[aba],长度为3。

本节给出三种不同的实现方式,并对比分析每种方法的复杂度,内容如下:

==基本算法==

==DP方案==

==后缀数组==

==各方法复杂度分析==

==================================

基本算法

其实对于最长公共子串,还是比较简单易想的,因为子串是连续的,这就方便了很多。最直接的方法就是用X每个子串与Y的每个子串做对比,求出最长的公共子串。代码如下:

#include <iostream>
#include <string>
using namespace std;int tmplen(char* x,char* y){int len = 0;while(*x && *y && *x++ == *y++){//注意边界值的可能性len++;}return len;
}
int main(){string s1,s2;int maxlen = 0;int index = 0;cin >> s1 >> s2;for(int i = 0; i < s1.size(); i++){for(int j = 0; j < s2.size(); j++){int len = tmplen(&s1[i],&s2[j]);if(len > maxlen){maxlen = len;index = i;}}}for(int i = index; i < index+maxlen; i++){cout << s1[i] << " ";}cout << endl;return 0;
}

==================================

DP方案

既然最长公共子串是最长公共子序列的变体,那么最长公共子串是不是也可以用动态规划来求解呢?

我们还是像之前一样“从后向前”考虑是否能分解这个问题,在最大子数组和中,我们也说过,对于数组问题,可以考虑“如何将arr[0,...i]的问题转为求解arr[0,...i-1]的问题”,类似最长公共子序列的分析,这里,我们使用dp[i][j]表示 以x[i]和y[j]结尾的最长公共子串的长度,因为要求子串连续,所以对于X[i]与Y[j]来讲,它们要么与之前的公共子串构成新的公共子串;要么就是不构成公共子串。故状态转移方程

  1. X[i] == Y[j],dp[i][j] = dp[i-1][j-1] + 1
  2. X[i] != Y[j],dp[i][j] = 0

对于初始化,i==0或者j==0,如果X[i] == Y[j],dp[i][j] = 1;否则dp[i][j] = 0。

代码如下:

#include <iostream>
#include <string>
using namespace std;int main(){string s1,s2;int index = 0;int maxlen = 0;int dp[30][30] = {0};cin >> s1 >> s2;for(int i = 0; i < s1.size(); i++){for(int j = 0; j < s2.size(); j++){if(s1[i] == s2[j]){if(i == 0|| j == 0){dp[i][j] = 1;}else{dp[i][j] = dp[i-1][j-1] + 1;}if(dp[i][j] > maxlen){maxlen = dp[i][j];index = i;}}}}for(int i = index - maxlen + 1; i <= index; i++){cout << s1[i] << " ";}cout << endl;return 0;
}

==================================

后缀数组

前面提过后缀数组的基本定义,与子串有关,可以尝试这方面思路。由于后缀数组最典型的是寻找一个字符串的重复子串,所以,对于两个字符串,我们可以将其连接到一起,如果某一个子串s是它们的公共子串,则s一定会在连接后字符串后缀数组中出现两次,这样就将最长公共子串转成最长重复子串的问题了,这里的后缀数组我们使用基本的实现方式。

值得一提的是,在找到两个重复子串时,不一定就是X与Y的公共子串,也可能是X或Y的自身重复子串,故在连接时候我们在X后面插入一个特殊字符‘#’,即连接后为X#Y。这样一来,只有找到的两个重复子串恰好有一个在#的前面,这两个重复子串才是X与Y的公共子串。

代码如下:

/* 最长公共子串 后缀数组 */
char * suff[100];
int pstrcmp(const void *p, const void *q)
{
     return strcmp(*(char**)p,*(char**)q);
}
int comlen_suff(char * p, char * q)
{
     int len = 0;
     while(*p && *q && *p++ == *q++)
     {
         ++len;
         if(*p == '#' || *q == '#')
         {
             break;
         }
     }
     int count = 0;
     while(*p)
     {
         if(*p++ == '#')
         {
             ++count;
             break;
         }
     }
     while(*q)
     {
         if(*q++ == '#')
         {
             ++count;
             break;
         }
     }
     if(count == 1)
         return len;
     return 0;
}
void LCS_suffix(char * X, int xlen, char * Y, int ylen)
{
     int suf_index = maxlen = maxindex = 0;
     int len_suff = xlen + ylen + 1;
     char * arr = new char [len_suff + 1];  /* 将X和Y连接到一起 */
     strcpy(arr,X);
     arr[xlen] = '#';
     strcpy(arr + xlen + 1, Y);
     for(int i = 0; i < len_suff; ++i)  /* 初始化后缀数组 */
     {
         suff[i] = & arr[i];
     }
     qsort(suff, len_suff, sizeof(char *), pstrcmp);
     for(int i = 0; i < len_suff-1; ++i)
     {
         int len = comlen_suff(suff[i],suff[i+1]);
         if(len > maxlen)
         {
             maxlen = len;
             suf_index = i;
         }
     }
     outputLCS(suff[suf_index]);
}

【可以参考之前的博客文章:后缀树】

各方案复杂度对比

设字符串X的长度为m,Y的长度为n,最长公共子串长度为l。

对于基本算法,X的子串(m个)和Y的子串(n个)一一对比,最坏情况下,复杂度为O(m*n*l),空间复杂度为O(1)。

对于DP算法,由于自底向上构建最优子问题的解,时间复杂度为O(m*n);空间复杂度为O(m*n),当然这里是可以使用滚动数组来优化空间的,滚动数组在动态规划基础回顾中多次提到。

对于后缀数组方法,连接到一起并初始化后缀数组的时间复杂度为O(m+n),对后缀数组的字符串排序,由于后缀数组有m+n个后缀子串,子串间比较,故复杂度为O((m+n)*l*lg(m+n)),求得最长子串遍历后缀数组,复杂度为O(m+n),所以总的时间复杂度为O((m+n)*l*lg(m+n)),空间复杂度为O(m+n)。

总的来说使用后缀数组对数据做一些“预处理”,在效率上还是能提升不少的。

最字头之二:最长公共字串相关推荐

  1. 求两个字符串的最长公共字串(连续)

    题目描述: 输入两个字符串,求其的最长的公共的字串,这与最长公共子序列不一样 输出两字符串的最长公共字串 思路一: 从字符串A开始遍历,同时遍历字符串A,找到第一个与当前字符串A相同的字符,此时记下当 ...

  2. 后缀数组以及利用后缀数组求取最长公共字串

    后缀树组是一个字符串的所有后缀的排序数组.后缀是指从某个位置 i 开始到整个串末尾结束的一个子串.字符串 r 的从 第 i 个字符开始的后缀表示为 Suffix(i) ,也就是Suffix(i)=r[ ...

  3. 最长公共字串(LCS)C++代码(动态规划实现)

    时间复杂度:O(n1)*O(n2),n1和n2为两个字符串的长度 注释掉#define TEST取消随机输入模式 #define TEST #include<ctime> #include ...

  4. 最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr...

    问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X="x0,x1,-,xm-1",序列Y=& ...

  5. 字符串相关处理kmp,前缀数,后缀树,后缀数组,最长回文串,最长重复字串,最长非重复字串

    1. 最长回文串 一般用后缀数组或者后缀树可以解决, 用此方法:http://blog.csdn.net/v_july_v/article/details/6897097 预处理后缀树,使得查询LCA ...

  6. 最字头之一:最长公共子序列

    转载自:http://blog.csdn.net/yysdsyl/article/details/4226630 请支持原PO的博客,非常精彩! 动态规划法 经常会遇到复杂问题不能简单地分解成几个子问 ...

  7. 计算最长公共数字串个数

    一.华为OJ题目(计算最长公共数字个数) 样例输出 输出123058789,函数返回值9 输出54761,函数返回值5 接口说明 函数原型: unsignedint Continumax(char** ...

  8. 求两个字符串的最大连续公共字串

    如下两个字符串,公共连续字符串为abcdf,求出这个abcdf, "aaffffsfabcdfasf", "aaaadfsabcdfsdb" 假设字符串长度分别 ...

  9. L2-008 最长对称字串 以下标i展开

    L2-008. 最长对称子串 时间限制 100 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 对给定的字符串,本题要求你输出最长对称子串的长度. ...

最新文章

  1. 深度学习--TensorFlow(7)拟合(过拟合处理)(数据增强、提前停止训练、dropout、正则化、标签平滑)
  2. 有哪些适合远程办公的软件值得推荐?
  3. 信号转换| 如何使用模拟电路完成对方波信号倍频PWM转换?
  4. slf4j 使用方法---个人总结
  5. 微型计算机原理 考试试题,微机原理期末考试试卷(有答案)
  6. UA MATH571B 试验设计III 单因素试验设计3
  7. 计算机基础课程金课建设,大学计算机基础金课建设探索与实践
  8. ibatis Parameter index out of range (1 number of parameters, which is 0)
  9. 用千元左右的手机你会感到没面子吗?网友回答令人赞叹
  10. 《软件工程》课堂作业:返回一个整数数组中最大字数组的和
  11. JavaScript 运行机制详解:再谈Event Loop
  12. 如何高效学python编程_如何高效自学编程?天下没有难学的python
  13. meta是什么意思中文_专访 | 从艾希到电子姬,幻刃网络做了款TapTap 8.2分的Meta手游...
  14. net core 通过js打印集合(创建树)
  15. oracle异常:主动抛出自定义异常+捕获指定异常
  16. java经典算法(五)---zws
  17. 无法解析类型 javax.swing.JComponent。从必需的 .class 文件间接引用了它
  18. MSP430F5438A+TM1650四位数码管显示+16位AD转换器ADS1110
  19. 00003__爬拉勾网
  20. 网络科学论坛纪要-2012

热门文章

  1. CMCC-EDU一个帐号,多设备共享解决方案
  2. keil MDK启动文件分析---基于LPC2100系列(其实都是相通的)
  3. test.ctf8(XSS挑战之旅)解题记录
  4. 最美手机开售 魅族魅蓝X曜石黑1699元
  5. H3C 交换机软件版本升级详细步骤
  6. TP_link路由器设置
  7. 堆和栈的区别(转过无数次的文章) 1
  8. instanceof 运算符
  9. 基频抖动Jitter
  10. jquery 删除元素的onclick事件