最大公共子序列,最大公共子串,最大回文子串

本文介绍如何利用动态规划解决最长公共子序列、最长公共子串及最长回文子串问题,并提供C/C++实现代码。通过实例解析算法原理与步骤。

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

1、两个字符串中最长公共子序列

该问题是动态规划的经典问题,找出的公共序列不一定是连续的,参考资料很多,这里就贴出公式和代码,做个简单的笔记。

设序列a,b的长度分别为n和m,L(i,j)为a(i),b(j)的最长公共子序列长度,有递推公式:

时间复杂度为O(mn),代码如下:

#define N 50
intlcs(char *a, char *b, char *c)
{

	inti,j,k;
	intn,m;
	intmz[N+1][N+1],s[N+1][N+1];
	n=strlen(a);
	m=strlen(b);
	memset(mz,0,sizeof(mz));
	memset(s,0,sizeof(s));

	for(i=1;i<n+1;i++)
	{
		for(j=1;j<m+1;j++)
		{
			if(a[i-1]==b[j-1])
			{
				mz[i][j]=mz[i-1][j-1]+1;
				s[i][j]=0;
			}
			else
			{
				if(mz[i-1][j]>=mz[i][j-1])
				{
					mz[i][j]=mz[i-1][j]; //说明a[i]不在公共子序列中
					s[i][j]=-1;
				}
				else
				{
					mz[i][j]=mz[i][j-1]; //说明b[i]不在公共子序列中
					s[i][j]=1;
				}
			}
		}
	}
	
	i=n,j=m;
	k=mz[n][m];
	c[k--]='\0';
	while(i>0 && j>0)
	{
		switch(s[i][j])
		{
		case -1:
			i--;
			break;
		case 0:
			c[k--]=a[i-1];
			i--,j--;
			break;
		case 1:
			j--;
			break;
		default:
			break;
		}
	}
	returnmz[n][m];
}

2、两个字符串中最长公共子串

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

LCS(S[1:i],T[1,j])=LCS(S[1:i],T[1:j])+S[i]    S[i]=T[j]

                             =””                     S[i]!=T[j]

参考文献:http://blog.youkuaiyun.com/liufeng_king/article/details/8528858

代码如下:

string getLCS(string &s, string&t) 

{ 
    int q=s.length();
    int p=t.length();
    int i,j;
    string **num = new string *[q];    
    for(i=0;i<q;i++)     
    {     
        num[i] = new string[q];   
    }    
    char char1 = '\0'; 
    char char2 = '\0';  
    int len = 0; 
    string lcs = "" ; 
    for(i=0; i<q; i++) 
    { 
        for (j=0; j<p; 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(i=0;i<q;i++)     
    {     
        delete[] num[i];  
    }  
    delete[] num;
    return lcs; 
}

3、最长回文子串

有了最长公共子串的方法,那么求字符串中最长回文子串也变得简单了,只需要把原字符串反转,然后与求最长公共子串即可。代码如下:

//回文字符串
string getLCS(string &s)  
{  
    int q=s.length();
    string t=s; //反转字符串,找出s,t的最大公共子串    
    int i,j;
    for (i = 0; i<q/2; i++)
    {
        char temp = t[i];
        t[i] = t[q-i-1];
        t[q-i-1] = temp;
    }
    
    string **num = new string *[q];    
    for(i=0;i<q;i++)      
    {      
        num[i] = new string[q];    
    }     
    char char1 = '\0';  
    char char2 = '\0';  
    int len = 0;  
    string lcs = "" ;  
    for(i=0; i<q; i++)  
    {  
        for (j=0; j<q; 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(i=0;i<q;i++)      
    {      
        delete[] num[i];   
    }   
    delete[] num; 
    return lcs;  
}

时间复杂度O(n^2),当然还有一种复杂度只有O(n)方法,详见参考文献:

http://www.akalin.cx/longest-palindrome-linear-time

http://bbs.dlut.edu.cn/bbstcon.php?board=Competition&gid=23474

http://blog.youkuaiyun.com/sgbfblog/article/details/7979541


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值