题解:P3333 [ZJOI2013] 丽洁体
Justin0779 · · 题解
纪念校内模拟赛场切紫(虽然是水紫)
Solution
我们先考虑一些必要的操作。由于我们并不好操作单词的匹配,所以想到把单词映射处理,这里哈希完全可以用 map 来代替。处理时只需要把每个单词加进 map 里即可。
现在我们把问题转化为了:
给定某四个个正整数序列
T 、A 、B 、C ,删除T 中的一些数,使T 变为A^{*}B^{*}C 的形式,也就是A 、B 、C 与某些字符串S 按顺序拼接而成的T^{'} ,求删除的最小次数,保证答案一定存在。
这样,我们把问题转化成了三个子问题:
- 将
T 的左边变成A 的最小操作次数。 - 将
T 的右边变成C 的最小操作次数。 - 将 1、2 操作完后的
T^{'} 中间删出一个B 的最小操作次数。
对于 1、2,我们很容易得到能删尽量删,能匹配尽量匹配为最优的结论,因为
但是对于 3,我们的策略便失效了。因为很可能利用现在的左端点
I can love you as love myself forever
I
love myself
forever
这样答案是
所以我们考虑更换策略。
值得注意的是,当存在端点
一个很好的想法是双指针,这样复杂度能做到绝妙的
看到数据范围
于是我们就这样通过了本题。
Code
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 514;
namespace Otakus
{
string T, A, B, C, str;
unordered_map<string, int> dict;
int a[N], b[N], c[N], t[N], cnt;
int atp, btp, ctp, ttp;
int vis[N], minn, Justin = N;
vector<int> nxt[N];
void solve()
{
int len, ans = 0, i = 1, j = ttp; // and i is the left, j is the right
// for A
len = 0;
while (len < atp)
{
if (t[i] == a[len + 1])
len++;
else
ans++;
i++;
}
// for C
len = ctp;
while (len)
{
if (t[j] == c[len])
len--;
else
ans++;
j--;
}
// now, i ~ j to find B
for (int l = i, r = i; (l <= j - btp + 1 && r <= j); l++) {
if (t[l] != b[1])
continue;
len = 0, r = l;
while (len < btp && r <= j)
{
if (t[r] == b[len + 1])
len++;
r++;
}
if (len == btp)
Justin = min(Justin, r - l - btp);
else
break;
}
cout << ans + Justin;
}
void init()
{
getline(cin, T);
getline(cin, A);
getline(cin, B);
getline(cin, C);
str = "";
for (int i = 0; i < T.length(); i++)
{
if (T[i] == ' ') {
if (!dict.count(str))
dict[str] = ++cnt;
t[++ttp] = dict[str];
str = "";
}
else if (T[i] - 'a' >= 0 && T[i] - 'a' < 26)
str += T[i];
}
if (!dict.count(str))
dict[str] = ++cnt;
t[++ttp] = dict[str];
str = "";
for (int i = 0; i < A.length(); i++)
{
if (A[i] == ' ')
a[++atp] = dict[str], str = "";
else if (A[i] - 'a' >= 0 && A[i] - 'a' < 26)
str += A[i];
}
a[++atp] = dict[str];
str = "";
for (int i = 0; i < B.length(); i++)
{
if (B[i] == ' ')
b[++btp] = dict[str], str = "";
else if (B[i] - 'a' >= 0 && B[i] - 'a' < 26)
str += B[i];
}
b[++btp] = dict[str];
str = "";
for (int i = 0; i < C.length(); i++)
{
if (C[i] == ' ')
c[++ctp] = dict[str], str = "";
else if (C[i] - 'a' >= 0 && C[i] - 'a' < 26)
str += C[i];
}
c[++ctp] = dict[str];
solve();
}
} // namespace Otakus
int main()
{
// freopen("main.in", "r", stdin);
// freopen("main.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
Otakus::init();
return 0;
}
最后,我认为降绿,理由如下:
1.此题的难度仅在于 STL 的运用以及字符串的读入。
2.数据不够强。如果真的卡到适当的、只允许双指针的精妙做法过那么紫就无可置疑。