题目描述

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

 

输入描述:

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

输出描述:

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

示例1

输入

abcbaa 2

输出

2

说明

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

思路:若是任意两个位置可以交换的话,是可以直接二分答案求解的,而本题限制只能相邻两个位置进行交换,一种较优的解法是采用动态规划。

我们首先需要预处理一些东西,首先是每种小写字符分别在那些位置出现过,我们采用一个26*n的数组来存储,之后定义n*n的dp二维数组,并令dp[l][r]表示相应小写字符第l个到第r个连续的最小移动次数。我们发现dp[l][l+1]=v[l+1]-v[l]-1,其中v数组为存储相应小写字母出现的位置,并且我们发现dp[l][r]是可以采用区间dp的实现进行转移的,其上一个状态是dp[l+1][r-1]并且由于将i位置与j位置移动到一起需要(v[j]-v[i]-1)次,但是由于区间内已经有了移动好的(j-i-1)个字母,所以可以少移动这么多次,固需要减去这个数字,所以动态转移方程可以写作:dp[i][j] = dp[i+1][j-1]+(v[j]-v[i]-1)-(j-i-1)。

#include<vector>
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
string s;
int m;
int work(vector<int> q){
    int n=q.size();
    vector<vector<int>> dp(n,vector<int>(n,0));
    for(int i=0;i<n-1;i++)
        dp[i][i+1]=q[i+1]-q[i]-1;
    for(int j=2;j<n;j++)
        for(int i=0;i<n-j;i++){
            int l=i,r=i+j;
            dp[l][r]=dp[l+1][r-1]+q[r]-q[l]-(r-l);
        }
    int res=0;
    for(int i=0;i<n;i++)
        for(int j=i;j<n;j++)
            if(dp[i][j]<=m)
                res=max(res, j-i+1);
    return res;
}
int main(void){
    cin >> s >> m;
    vector<vector<int>> arr(26);
    for(int i=0;i<s.size();i++)
        arr[s[i]-'a'].push_back(i);
    int mx=0;
    for(int i=0;i<26;i++)
        mx=max(mx, work(arr[i]));
    printf("%d\n",mx);
    return 0;
}