回文子串公共子序列问题一招解决

本文介绍了如何求解两个字符串的最长公共子序列,通过动态规划实现,给出了示例代码。同时,讲解了判断字符串对称性的方法,以及计算使字符串变为回文串所需的最小插入次数的算法。内容涵盖字符串处理和动态规划基础。

最长公共子序列: 

给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列

示例1

输入:

"1A2C3D4B56","B1D23A456A"

复制返回值:

"123456"

解题思路:

#include<iostream>
#include<algorithm>
#include<string.h>
char a[1001],b[1001];
int lcs[1001][1001];

using namespace std;
main()
{
	gets(a+1);//为了更加方便所以从1开始取值,例如你输入hnucm a[0]是空格,a[1]才是h,这就
//更好的操作和使用
	gets(b+1);
	string dp="";
	int l1=strlen(a+1);
	int l2=strlen(b+1);
	for(int i=0;i<=l1;i++)
	   for(int j=0;j<=l2;j++)
	   { if(i==0||j==0) lcs[i][j]=0;
	     else if(a[i]==b[j]) lcs[i][j]=lcs[i-1][j-1]+1;
	   	 else lcs[i][j]=max(lcs[i-1][j],lcs[i][j-1]);	   
		}
		int ans=l1,bns=l2,num=0;
		while(ans!=0&&bns!=0)//恢复最长公共序列,相当于从一串字符串中找到A、B两串中共有的字符,这里从A串中还原最长公共字符串
		{
			if(lcs[ans][bns]!=lcs[ans-1][bns])//lcs[i][j]来自于lcs[i][j-1]或者lcs[i-1][j-1],不管来自哪个,Ax肯定包含在最长公共字符串中
			 {
			 	 dp=dp+a[ans];//记录最长公共串
			 	 ans--;//继续判断A串中前一个字符
			 	 bns--;//不管By来自与哪个表达式,By已经被判断过了,现在应该继续B的前一个字符
			 }
			 else if(lcs[ans][bns]==lcs[ans][bns-1])//如果来自lcs[i][j-1],那么说明By不包含在最长公共字符串中,继续遍历B的前一个字符
			       bns--;
			else ans--;//说明Ax不包含在最长公共字符串中,继续遍历A的前一个字符
			  
			  
		}
		reverse(dp.begin(),dp.end());//因为给dp赋值时就是倒着来的
		cout<<dp;
}

 

 

牛客AC代码:

注意上述图片中是重下标1开始的哦!!!!

class Solution {
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        // write code here
        int lcs[2000][2000];
        string dp="";
        int l1=s1.size();
        int l2=s2.size();
        if(l1==0||l2==0)return "-1";
        
        for(int i=1;i<=l1;i++)
	   for(int j=1;j<=l2;j++)
	   { 
           if(i==0||j==0)lcs[i][j]=0;
	    else if(s1[i-1]==s2[j-1]) lcs[i][j]=lcs[i-1][j-1]+1;//注意这里和上一个代码的区别
	   	 else lcs[i][j]=max(lcs[i-1][j],lcs[i][j-1]);	   
		}
        
        int ans1=l1,ans2=l2;
        while(ans1!=0&&ans2!=0){
            if(lcs[ans1][ans2]!=lcs[ans1-1][ans2]){
                dp=dp+s1[ans1-1]; //因为牛客中是直接一个字符串,所以我们就必须要和前面的保持一致          
                ans1--;
                ans2--;
            
            }
            else if(lcs[ans1][ans2]==lcs[ans1][ans2-1])
                ans2--;
            else ans1--;           
}
       if(dp.size()==0)return "-1";
      reverse(dp.begin(),dp.end());
        return dp;
    }
};

 回文串:

对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。

输入格式:

输入在一行中给出长度不超过1000的非空字符串。

输出格式:

在一行中输出最长对称子串的长度。

输入样例:

Is PAT&TAP symmetric?

输出样例:

11
#include<iostream>
#include<cstring>
using namespace std;

int main()
{
    string str;
    getline(cin,str);//读入空格,不能用cin

    int res=0;
    for(int i=0;i<str.size();i++)//注意下面两种情况必须是一起的,为什么呢,因为题目中含有空格,而空格会影响最大的对称长度,你可以试一试
    {
        int l=i-1,r=i+1;//长度是奇数的情况
        while(l>=0&&r<str.size()&&str[l]==str[r]) //l r没有出界
            l--,r++;//l向左走 r向右走
        res=max(res,r-l-1);//长度是r-l+1 -2(减去两边不对称的l r)=r-l-1

       l=i,r=i+1;//长度是偶数的情况        
	   while(l>=0&&r<str.size()&&str[l]==str[r]) 
            l--,r++;
       res=max(res,r-l-1);
    }
    cout<<res<<endl;
    
    return 0;
}

再来一个回文串 

对字符串s[i..j],最少需要进行dp[i][j]次插入才能变成回文串。当i == jdp[i][j] = 0,因为当i == js[i..j]就是一个字符,本身就是回文串,所以不需要进行任何插入操作。

int minInsertions(string s) {
    int n = s.size();
    // 定义:对 s[i..j],最少需要插入 dp[i][j] 次才能变成回文
    vector<vector<int>> dp(n, vector<int>(n, 0));
    // base case:i == j 时 dp[i][j] = 0,单个字符本身就是回文
    // dp 数组已经全部初始化为 0,base case 已初始化
 
    // 从下向上遍历
    for (int i = n - 2; i >= 0; i--) {
        // 从左向右遍历
        for (int j = i + 1; j < n; j++) {
            // 根据 s[i] 和 s[j] 进行状态转移
            if (s[i] == s[j]) {
                dp[i][j] = dp[i + 1][j - 1];
            } else {
                dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
            }
        }
    }
    // 根据 dp 数组的定义,题目要求的答案
    return dp[0][n - 1];
}

 详解问题三以及一些列子串问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H-rosy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值