题目
链接:https://www.nowcoder.com/question/next?pid=8537228&qid=141031&tid=22230511
**Level: ** 2018算法工程师笔试题
Discription:
字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?
第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)
一个非负整数,表示操作之后,连续最长的相同字母数量。
Example 1:
Input: abcbaa 2
Output: 2
Note:
- 使2个字母a连续出现,至少需要3次操作。即把第1个位置上的a移动到第4个位置。
所以在至多操作2次的情况下,最多只能使2个b或2个a连续出现。
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
vector<vector<int>> loc(26); //二维数组初始化
int mem[1001][1001];
int dp(int start, int end, vector<int> loc)
{
if (start == end || start == (end + 1) || start > end)
{
mem[start][end] = 0;
return 0;
}
else if (mem[start][end] != -1)
return mem[start][end];
else
{
mem[start][end] = dp(start + 1, end - 1, loc) + loc[end] - loc[start] - (end - start);
return mem[start][end];
}
}
int main()
{
string str;
int m;
cin >> str >> m;
for (int i = 0; i < str.length(); i++)
loc[str[i] - 'a'].push_back(i);
int nums[26] = { 0 };
for (int i = 0; i < 26; i++)
{
fill(mem[0], mem[0] + 1001 * 1001, -1);
if (loc[i].size() >= 1)
nums[i] = 1;//至少有一个连续的
for (int j = 0; j < loc[i].size(); j++)
{
for (int k = j + 1; k < loc[i].size(); k++)
{
int len = k - j + 1;
int lx = dp(j, k, loc[i]);
if (lx <= m && len > nums[i])
nums[i] = len;
}
}
}
sort(nums, nums + 26);
cout << nums[25] << endl;
system("pause");
return 0;
}
思考
- 算法时间复杂度为O((S^3)),空间复杂度为O((S^2) ),S是字符串的长度,递归最深是S/2。
- DP实现,转移方程是dp(start, end, loc) = dp(start + 1, end - 1, loc) + loc[end] - loc[start] - (end - start);最优的操作总是两边向中间聚合。DP的难点在于想通如何把大问题化解成为小问题,其次是想明白小问题的最优操作是怎样的,比如这题中的两边向中间聚合,如果这一步想不通,即使化解成为小问题也求解不出来。
- 光用递归实现会超时,定义一个数组来存放中间重复计算的值。