牛客-字母交换

目录

题目要求

输入描述:

输出描述:

示例1

说明

解题思路

代码


题目要求

字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?

输入描述:

第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)

输出描述:

一个非负整数,表示操作之后,连续最长的相同字母数量。

示例1

输入

abcbaa 2

输出

2

说明

使3个字母a连续出现,至少需要3次操作。即把第1个位置上的a移动到第4个位置。所以在至多操作2次的情况下,最多只能使2个b或2个a连续出现。


解题思路

      动态规划:使用方阵dp[i][j],表示在str[x[i]]到str[x[j]]之间的所有此字母移动到相邻位置需要的最小交换次数。

首先,对于任一字母,任意依次出现的两个字母,移动到相邻位置,需要的最小交换次数,是他们下标之差减一。即:dp[i][i+1]=(x[i+1]-x[i])-1。

进一步,对于任一字母,任意两次出现的两个字母,移动到相邻位置,需要的最小交换次数,显然也是他们下标之差减一。即:dp[i][i+k]=(x[i+k]-x[i])-1。

最终,对于任一字母,任意两次出现的两个字母及其之间的所有此字母,移动到相邻的位置,需要的最小交换次数,是其之间的所有此字母移动到相邻位置所需要的最小交换次数,加上两头两个字母移动到相邻位置所需要的最小交换次数,再减去两头两个字母之间此字母的个数。即:dp[left][right]=dp[left+1][right-1]+(x[right]-x[left]-1)-(right-left-1)。

找到这个规律后,遍历从'a'~'z'每个字母,就能较为快速的计算出至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同了。

代码及其超级详细注解如下:

代码

#include <iostream>
#include<vector>
#include<stdlib.h>
using namespace std;
int m;
string str;
/*此函数用来计算字符串中的某个字母,
在最多m次交换后,此字母连续的最大长度*/
int maxi(vector<int>& x){
  //此字母出现的次数
  int n=x.size();
  /*创建方阵,其中dp[i][j]表示在str[x[i]]到str[x[j]]
  之间的所有此字母移动到相邻位置需要的最小交换次数*/
  int dp[n][n];
  //初始化方阵为0
  for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
        dp[i][j]=0;
    }
  }
  for(int i=0;i<n-1;i++){
    /*初始化方阵,即:依次出现的两个字符串变为相邻,
    需要的最少交换次数是此字符的下标之差减一*/
    dp[i][i+1]=(x[i+1]-x[i])-1;
  }
  //k表示步长
  for(int k=2;k<n;k++){
    //用i遍历所有字母
    for(int i=0;i<n-k;i++){
     //第i次出现,第i+k次出现
     int left=i,right=i+k;
     dp[left][right]=dp[left+1][right-1]+(x[right]-x[left]-1)-(right-left-1);
     /*使得第left次出现到第right次出现之间的所有此字母连续所需要的最小
     交换次数,等于第left+1次出现到第right-1次出现之间的所有此字母连续
     所需要的最小交换次数,加上第left次出现和第right次出现移动到一起所
     需要的交换次数,再减去此字母在这两次出现之间出现的次数*/
   }
  }
  int max=1;
  for(int i=0;i<n;i++){
    for(int j=i+1;j<n;j++){
        if(dp[i][j]<=m&&(j-i+1)>max){
        //遍历方阵找出经过小于等于m次交换,此字母连续的最大长度
          max=j-i+1;
        }
    }
  }
  /*for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
        cout<<dp[i][j]<<" ";
    }
    cout<<endl;
  }
  cout<<endl;*/
  return max;
}
int main()
{
    cin >> str >> m;
    //为每个字母创建一个容器,依次存放其出现在字符串中的下标
    vector< vector<int> > a(26);
    //初始化每个字母的位置容器
    for(int i=0;i<str.length();i++){
      a[str[i]-'a'].push_back(i);
    }
    int max=0;
    //遍历每个字母,找出所有字母,经过小于等于m次交换后,此字母连续的最大长度
    for(int i=0;i<26;i++){
     if(maxi(a[i])>max) max=maxi(a[i]);
    }
    cout<<max<<endl;
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值