解题思路
错解
贪心:每次都移动至当前最近的对应方块上。
反例:
s
=
s =
s= abxac
t
=
t =
t= abac
贪心结果(下标) 0 → 1 → 0 → 4 0 \rightarrow 1 \rightarrow 0 \rightarrow 4 0→1→0→4,答案为 5 5 5。
正确结果(下标) 0 → 1 → 3 → 4 0 \rightarrow 1 \rightarrow 3 \rightarrow 4 0→1→3→4,答案为 4 4 4。
答案与逾期不符合,故贪心解法不正确。
正解
首先,我们注意到数据范围的最后一句话,数据保证随机,那么这样每个字符的数量约为 n 26 \dfrac n {26} 26n。
当我们要在键盘串查找一种字符的位置时,
O
(
n
)
O(n)
O(n) 遍历效率较低,可以考虑先将字符串进行预处理,将字符 a
的下标全部存入
g
[
0
]
g[0]
g[0],字符 b
的下标存入
g
[
1
]
g[1]
g[1],
…
\dots
…
设 f [ x ] f[x] f[x] 表示当前情况下,以 s [ x ] s[x] s[x] 作为结尾字符,键盘指针指向 x x x,构成字符串的最小代价。例如,设键盘串为 a b c d e f abcdef abcdef, f [ 3 ] f[3] f[3] 表示构成 a b c d abcd abcd,且最终键盘指针指向 3 3 3 的最小代价。
计算出字符串 abcc
所需要的步数时:
- 我们可以先计算构成
a
的所有最短步数,键盘串中一定存在a
,设其中一个为a
的下标为 x x x,那么 f [ x ] = 0 f[x] = 0 f[x]=0,由于 f f f 数组定义在全局,故此处在代码中不体现。 - 接下来计算构成
ab
的所有最短步数,由于我们已经计算出了构成a
的所有最短步数,那么我们可以暴力枚举所有a
的位置,与所有b
的位置,假设其中一个a
的位置为 y y y,其中一个b
的位置为 x x x,那么 f [ x ] = m i n ( f [ x ] , f [ y ] + a b s ( x − y ) ) f[x] = min(f[x], f[y] + abs(x - y)) f[x]=min(f[x],f[y]+abs(x−y))。 - 计算所有构成
abc
的做法如上。 - 计算所有构成
abcc
的最短步数,由于 t [ 3 ] = t [ 2 ] t[3] = t[2] t[3]=t[2],故本轮可跳过。
时间复杂度 O ( m ( n 26 ) 2 ) O(m(\dfrac n {26})^2) O(m(26n)2)。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e3 + 10, INF = 0x3f3f3f3f;
int n, m, t;
string s, str;
vector<int> g[26];
int f[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> t >> s >> str;
for (int i = 0; i < n; ++ i )
g[s[i] - 'a'].push_back(i);
for (int i = 1; i < m; ++ i )
{
int u = str[i] - 'a', last = str[i - 1] - 'a';
if (u != last)
{
int res = INF;
bool find = false;
for (auto x: g[u])
{
for (auto y: g[last])
res = min(res, f[y] + abs(x - y));
f[x] = res;
if (f[x] <= t)
find = true;
}
if (!find)
{
cout << i << endl;
return 0;
}
}
}
cout << m << endl;
return 0;
}