动态规划之——字符串

1.最长回文子串

题目链接

中心遍历法:

1.外层循环遍历字符串,以每个字符串为中心点,向两边扩展,判断分为以下几种情况

s[left]==s[i] : 表示左边和中心点相等 left–

s[right]==s[i]:表示右边和中心点相等 right++

s[left]==s[right]:表示左边和右边相等 left-- right++

经过几次判断之后,left指向回文串的首部的前一个位置,right指向回文串尾部的后一个位置 -> 回文串长度= right-left-1

class Solution {
public:
    string longestPalindrome(string s) {
        int max=0;
        string str;
        for(int i=0;i<s.size();i++)
        {
            int left=i;
            int right=i;

            while(left>=0&&s[left]==s[i])
                left--;
            while(right<s.size()&&s[right]==s[i])
                right++;
            
            while(left>=0&&right<s.size()&&s[left]==s[right])
            {
                left--;
                right++;
            }
            int len=right-left-1;//回文串长度

            if(len>max)
            {
                max=len;
                string temp(s.begin()+left+1,s.begin()+right);
                str=temp;
            }
        }
        return str;
    }
};

动态规划法:

设dp[i][j] 表示从第j个字符至第i个字符是否是回文字符串,-> 当i==j时只有一个字符,则是字符串

当s[i]==s[j]时表示字符串的两端是相等的,那么dp[i-1][j+1]是回文串,那么dp[i][j]也是回文串

此时需要注意的是:当s[i]==s[j]并且只有两个或者三个字符时,说明是回文串,即i-1<=j+1时表示只有两个或者三个字符串

又我们是从短的字符串开始向后推的,即长的字符串的状态是需要短子字符串的状态来确定的,因此第一层循环时从前往后,第二层循环时从后往前

image-20210517194728567

class Solution {
public:
    string longestPalindrome(string s) {
        //dp[i][j]表示i-j是否为回文串
        //-> s[i]==s[j]&&dp[i][j-1]==true 则是回文串 
        //当只有一个字符时,肯定是回文串
        //当只有两个字符时,则需要判断这两个字符是否相等
        
        vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
        for(int i=0;i<s.size();i++)//字符串逐渐边长
        {
            for(int j=i;j>=0;j--)
            {
                if(i==j)//只有一个字符
                    dp[i][j]=true;
                else if(s[i]==s[j]&&(j+1>=i-1||dp[i-1][j+1]==true))//j+1>=i-1表示只有2个或者3字符
                    dp[i][j]=true;
            }
        }
        int left=0;
        int right=0;
        int max=0;
        for(int i=0;i<s.size();i++)
        {
            for(int j=i;j>=0;j--)
            {
                if(dp[i][j]==true)
                {
                    if(i-j+1>max)
                    {
                        max=i-j+1;
                        left=j;
                        right=i;
                    }
                }
            }
        }

        string str(s.begin()+left,s.begin()+right+1);
        return str;
    }
};

2.最长回文子序列

题目链接

给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000

注:回文子序列不是子串,因此可以不是连续的

在这里插入图片描述

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        //dp[i][j]表示j-i的回文串之中字符的个数
        vector<vector<int>> dp(s.size(),vector<int>(s.size(),0));
        int max=0;
        for(int i=0;i<s.size();i++)
        {
            for(int j=i;j>=0;j--)
            {
                if(i==j)
                    dp[i][j]=1;//一个字符时
                else if(s[i]==s[j])
                    dp[i][j]=dp[i-1][j+1]+2;//中间回文串的个数+2
                else//不是回文串
                    dp[i][j]=fmax(dp[i][j+1],dp[i-1][j]);//不在i-j区间之内,因此往上返回一层

                max=fmax(max,dp[i][j]);
            }
        }
        return max;     
    }
};

3.查找两个字符串a,b中的最长公共子串

题目链接

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
  string str1;
  string str2;
  while(cin>>str1)
  {
    cin>>str2;
    if(str1.size()<str2.size())
        str1.swap(str2);//保证str1为较长串
    int len1=str1.size();
    int len2=str2.size();
    string str;
    
    vector<vector<int>>dp(len2+1,vector<int>(len1+1,0));
    
    for(int i=0;i<len2;i++)
    {
      for(int j=0;j<len1;j++)
      {
        if(str2[i]==str1[j])
        {
          dp[i+1][j+1]=dp[i][j]+1;
          if(dp[i+1][j+1]>str.size())//新的较长子串
          {
            int begin=i-dp[i+1][j+1]+1;//相同子串的起始位置
            string temp(str2.begin()+begin,str2.begin()+i+1);
            str.swap(temp);
          }
        }
        else
          dp[i+1][j+1]=0;//不相等
      }
    }
    cout<<str<<endl;
  }
  return 0;
}

空间优化:

本层只和上一层有关,既可以进行空间优化

image-20210517191608911

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
  string str1;
  string str2;
  while(cin>>str1)
  {
    cin>>str2;
    if(str1.size()<str2.size())
        str1.swap(str2);//保证str1为较长串
    int len1=str1.size();
    int len2=str2.size();
    string str;
    vector<int>dp(len1+1,0);
    
    for(int i=0;i<len2;i++)
    {
      for(int j=len1-1;j>=0;j--)//为了避免数据覆盖,从后往前进行遍历
      {
        if(str2[i]==str1[j])
        {
          dp[j+1]=dp[j]+1;
          if(dp[j+1]>str.size())//新的较长子串
          {
            int begin=i-dp[j+1]+1;//相同子串的起始位置
            string temp(str2.begin()+begin,str2.begin()+i+1);
            str.swap(temp);
          }
        }
        else
          dp[j+1]=0;//不相等
      }
    }
    cout<<str<<endl;
  }
  return 0;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值