• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C语言求解最长公共子字符串问题及相关的算法分析

C语言求解最长公共子字符串问题及相关的算法分析

作者:hackbuteer1 字体:[增加 减小] 来源:互联网 时间:2017-05-28

hackbuteer1 通过本文主要向大家介绍了字符串匹配算法c语言,c语言字符串压缩算法,c语言字符串比较,c语言字符串输入,c语言字符串等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子序列,并打印出最长公共子序列。
例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子序列,则输出它们的长度4,并打印任意一个子序列。
分析:求最长公共子序列(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,因此一些重视算法的公司像MicroStrategy都把它当作面试题。

完整介绍动态规划将需要很长的篇幅,因此我不打算在此全面讨论动态规划相关的概念,只集中对LCS直接相关内容作讨论。如果对动态规划不是很熟悉,请参考相关算法书比如算法讨论。

考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bn-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:

(1) 如果am-1==bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;

(2) 如果am-1!=bn-1,则若zk-1!=am-1时,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;

(3) 如果am-1!=bn-1,则若zk-1!=bn-1时,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。

这样,在找A和B的公共子序列时,如果有am-1==bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。

求解:
引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定输出最长公共字串时搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] == Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。

问题的递归式写成:

201664173807574.gif (673×287)

回溯输出最长公共子序列过程:    

201664173831586.gif (674×501)

算法分析:
由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m + n)。

完整的实现代码如下:

/** 
找出两个字符串的最长公共子序列的长度 
** author :liuzhiwei  
** data  :2011-08-15 
**/  
#include "stdio.h" 
#include "string.h" 
#include "stdlib.h" 
int LCSLength(char* str1, char* str2, int **b) 
{ 
  int i,j,length1,length2,len; 
  length1 = strlen(str1); 
  length2 = strlen(str2); 
 
  //双指针的方法申请动态二维数组 
  int **c = new int*[length1+1];   //共有length1+1行 
  for(i = 0; i < length1+1; i++) 
    c[i] = new int[length2+1];   //共有length2+1列 
 
  for(i = 0; i < length1+1; i++) 
    c[i][0]=0;    //第0列都初始化为0 
  for(j = 0; j < length2+1; j++) 
    c[0][j]=0;    //第0行都初始化为0 
 
  for(i = 1; i < length1+1; i++) 
  { 
    for(j = 1; j < length2+1; j++) 
    { 
      if(str1[i-1]==str2[j-1])  //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素 
      { 
        c[i][j]=c[i-1][j-1]+1; 
        b[i][j]=0;     //输出公共子串时的搜索方向 
      } 
      else if(c[i-1][j]>c[i][j-1]) 
      { 
        c[i][j]=c[i-1][j]; 
        b[i][j]=1; 
      } 
      else 
      { 
        c[i][j]=c[i][j-1]; 
        b[i][j]=-1; 
      } 
    } 
  } 
  /* 
  for(i= 0; i < length1+1; i++) 
  { 
  for(j = 0; j < length2+1; j++) 
  printf("%d ",c[i][j]); 
  printf("\n"); 
  } 
  */ 
  len=c[length1][length2]; 
  for(i = 0; i < length1+1; i++)  //释放动态申请的二维数组 
    delete[] c[i]; 
  delete[] c; 
  return len; 
} 
void PrintLCS(int **b, char *str1, int i, int j) 
{ 
  if(i==0 || j==0) 
    return ; 
  if(b[i][j]==0) 
  { 
    PrintLCS(b, str1, i-1, j-1);  //从后面开始递归,所以要先递归到子串的前面,然后从前往后开始输出子串 
    printf("%c",str1[i-1]);    //c[][]的第i行元素对应str1的第i-1个元素 
  } 
  else if(b[i][j]==1) 
    PrintLCS(b, str1, i-1, j); 
  else 
    PrintLCS(b, str1, i, j-1); 
} 
 
int main(void) 
{ 
  char str1[100],str2[100]; 
  int i,length1,length2,len; 
  printf("请输入第一个字符串:"); 
  gets(str1); 
  printf("请输入第二个字符串:"); 
  gets(str2); 
  length1 = strlen(str1); 
  length2 = strlen(str2); 
  //双指针的方法申请动态二维数组 
  int **b = new int*[length1+1]; 
  for(i= 0; i < length1+1; i++) 
    b[i] = new int[length2+1]; 
  len=LCSLength(str1,str2,b); 
  printf("最长公共子序列的长度为:%d\n",len); 
  printf("最长公共子序列为:"); 
  PrintLCS(b,str1,length1,length2); 
  printf("\n"); 
  for(i = 0; i < length1+1; i++)  //释放动态申请的二维数组 
    delete[] b[i]; 
  delete[] b; 
  system("pause"); 
  return 0; 
} 

</div> </div>

201664174047130.gif (674×285)

第二种方法为:

/** 
找出两个字符串的最长公共子序列的长度 
** author :liuzhiwei  
** data  :2011-08-15 
**/  
#include "stdio.h" 
#include "string.h" 
#include "stdlib.h" 
int LCSLength(char* str1, char* str2)  //求得两个字符串的最大公共子串长度并输出公共子串 
{ 
  int i,j,length1,length2; 
  length1 = strlen(str1); 
  length2 = strlen(str2); 
 
  //双指针的方法申请动态二维数组 
  int **c = new int*[length1+1];   //共有length1+1行 
  for(i = 0; i < length1+1; i++) 
    c[i] = new int[length2+1];   //共有length2+1列 
 
  for(i = 0; i < length1+1; i++) 
    c[i][0]=0;    //第0列都初始化为0 
  for(j = 0; j < length2+1; j++) 
    c[0][j]=0;    //第0行都初始化为0 
 
  for(i = 1; i < length1+1; i++) 
  { 
    for(j = 1; j < length2+1; j++) 
    { 
      if(str1[i-1]==str2[j-1])  //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素 
        c[i][j]=c[i-1][j-1]+1; 
      else if(c[i-1][j]>c[i][j-1]) 
        c[i][j]=c[i-1][j]; 
      else 
        c[i][j]=c[i][j-1]; 
    } 
  } 
 
  //输出公共子串 
  char s[100]; 
  int len,k; 
  len=k=c[length1][length2]; 
  s[k--]='\0'; 
  i=length1,j=length2; 
  while(i>0 && j>0) 
  { 
    if(str1[i-1]==str2[j-1]) 
    { 
      s[k--]=str1[i-1]; 
      i--; 
      j--; 
    } 
    else if(c[i-1][j]<c[i][j-1]) 
      j--; 
    else 
      i--; 
  } 
  printf("最长公共子串为:"); 
  puts(s); 
 
  for(i = 0; i < length1+1; i++)  //释放动态申请的二维数组 
    delete[] c[i]; 
  delete[] c; 
  return len; 
} 
 
int main(void) 
{ 
  char str1[100],str2[100]; 
  int length1,length2,len; 
 
  printf("请输入第一个字符串:"); 
  gets(str1); 
  printf("请输入第二个字符串:"); 
  gets(str2); 
  length1 = strlen(str1); 
  length2 = strlen(str2); 
  len=LCSLength(str1,str2); 
  printf("最长公共子串的长度为:%d\n",len); 
  system("pause"); 
  return 0; 
} 

</div>        思路:跟上面的求2个字符串的公共子序列是一样的思路,只不过这里需要动态申请一个三维的数组,三个字符串的尾字符不同的时候,考虑的情况多一些而已。</div>
/** 
找出三个字符串的最长公共子序列的长度 
** author :liuzhiwei  
** data  :2011-08



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C语言求解最长公共子字符串问题及相关的算法分析
  • 一些C语言中字符串的算法问题解决实例小结
  • C语言中压缩字符串的简单算法小结
  • 字符串的组合算法问题的C语言实现攻略
  • C语言字符串快速压缩算法代码

相关文章

  • 2017-05-28深入理解atoi()与itoa()函数的用法
  • 2017-05-28C++中的运算符和运算符优先级总结
  • 2017-05-28C++的sstream标准库详细介绍
  • 2017-05-28VC++简单实现关机、重启计算机实例代码
  • 2017-05-28C语言的语法风格与代码书写规范指南
  • 2017-05-28浅析C语言头文件和库的一些问题
  • 2017-05-28C++中四种加密算法之DES源代码
  • 2017-05-28c++实现加载so动态库中的资源
  • 2017-05-28c语言实现二叉查找树实例方法
  • 2022-04-30C语言代码中的空白符

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • c++读取sqlserver示例分享
    • 教你5分钟轻松搞定内存字节对齐
    • vc获取计算机名和ip地址的方法
    • 在C++中反射调用.NET的方法(一)
    • 字符串拷贝函数memcpy和strncpy以及snprintf 的性能比较
    • C语言中结构体偏移及结构体成员变量访问方式的问题讨论
    • 构造函数不能声明为虚函数的原因及分析
    • C++线性时间的排序算法分析
    • C语言实现汉诺塔游戏
    • 获取本地网卡适配器信息具体代码

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有