一、题目描述
【编码题】字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?
输入描述:
第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)
输出描述:
一个非负整数,表示操作之后,连续最长的相同字母数量。
输入例子1:
abcbaa 2
输出例子1:
2
例子说明1:
使2个字母a连续出现,至少需要3次操作。即把第1个位置上的a移动到第4个位置。
所以在至多操作2次的情况下,最多只能使2个b或2个a连续出现。
二、解题思路
设置一个字符串长x26大小的二维数组,对于字符串中的每个字母,记录其出现的位置,将对应的矩阵元素设置为1,其余元素为0,然后按列优先遍历矩阵,将每个字母出现的位置下标记录到一个数组里,计算将i和j之间的相同元素全部移动到一起需要的最小移动次数,在这个最小次数满足满足约束条件的前提下,筛选出最大的连续字母的个数。最后比较所有字母的最大连续个数,输出其中的最大值即可。难点在于计算将i和j之间的相同元素全部移动到一起需要的最小移动次数,这里的核心思想就是使用动态规划得到转移方程,只要能想到这一点,问题就迎刃而解了。
三、AC代码
#include <iostream>
#include <string.h>
#include <algorithm>
#define N 26
using namespace std;
//dynamic programming
int dp(int i,int j,int a[])
{
if(i == j)
{
return 0;
}
else if(i+1 == j){
return a[j]-a[i]-1;
}
else{
//当一个字母出现多次且不连续时,应该从两侧往中间移,这样才能保证移动次数最少。找规律得到状态转移方程
return dp(i+1,j-1,a)+a[j]-a[i]-(j-i);
}
}
int main()
{
string str;
int num;
cin >> str >> num;
int length = str.length();
int a[length][N];
int b[N]; //存放各字母在满足约束的情况下最大的连续数
int m[length];
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(m,0,sizeof(m));
//lengthx26的矩阵,出现字母的地方都置1
for(int i=0;i<length;i++)
{
for(int j=0;j<N;j++)
{
a[i][str[i]-'a'] = 1;
}
}
for(int j=0;j<N;j++)
{
int k=0;
for(int i=0;i<length;i++)
{
if(a[i][j] == 1)
{
m[k++] = i;
}
}
if(k == 1)
{
b[j] = 1;
}
else
{
int temp=-1;
for(int i=0;i<k;i++)
{
for(int ii=i+1;ii<k;ii++)
{
if( dp(i,ii,m)<= num )
{
if((ii-i)+1> temp)
temp = (ii-i)+1;
}
}
}
b[j] = temp;
}
}
sort(b,b+N);
cout << b[N-1] << endl;
return 0;
}